diff --git a/.gitignore b/.gitignore index 789f65f..bee2749 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,8 @@ -.vs/ -Kraken.xcodeproj/xcuserdata -kraken_win/build/ -build/ +.vs/ +Kraken.xcodeproj/xcuserdata +kraken_win/build/ +build/ +kraken.dir/ +Win32/ +x64/ +kraken_win diff --git a/3rdparty/glad b/3rdparty/glad index 5831fb7..3ea1455 160000 --- a/3rdparty/glad +++ b/3rdparty/glad @@ -1 +1 @@ -Subproject commit 5831fb7741d2ab951a684b86c1570b77d7ab5cfb +Subproject commit 3ea14553f9e16cf5278bfbc922306d57ef8d11db diff --git a/3rdparty/glfw b/3rdparty/glfw index 23dfeee..0be4f3f 160000 --- a/3rdparty/glfw +++ b/3rdparty/glfw @@ -1 +1 @@ -Subproject commit 23dfeee4cb4fac69f0b1f3614c4305e2129cf43b +Subproject commit 0be4f3f75aebd9d24583ee86590a38e741db0904 diff --git a/hydra b/hydra index 00bb9b6..759b7af 160000 --- a/hydra +++ b/hydra @@ -1 +1 @@ -Subproject commit 00bb9b66892c1f3918c6ce83cf2e1c7cbe6c3948 +Subproject commit 759b7af066fa82890591ae24aea8c7d64fdf5379 diff --git a/kraken/CMakeLists.txt b/kraken/CMakeLists.txt index 3658da8..6115599 100644 --- a/kraken/CMakeLists.txt +++ b/kraken/CMakeLists.txt @@ -1,91 +1,81 @@ -include_directories(public) -add_subdirectory(public) -set(KRAKEN_PUBLIC_HEADERS "${KRAKEN_PUBLIC_HEADERS}" PARENT_SCOPE) - -add_sources(scalar.cpp) -add_sources(vector2.cpp) -add_sources(vector3.cpp) -add_sources(vector4.cpp) -add_sources(triangle3.cpp) -add_sources(quaternion.cpp) -add_sources(matrix4.cpp) -add_sources(aabb.cpp) -add_sources(hitinfo.cpp) - -# Private Implementation -add_sources(KRAmbientZone.cpp) -add_sources(KRAnimation.cpp) -add_sources(KRAnimationAttribute.cpp) -add_sources(KRAnimationCurve.cpp) -add_sources(KRAnimationCurveManager.cpp) -add_sources(KRAnimationLayer.cpp) -add_sources(KRAnimationManager.cpp) -add_sources(KRAudioBuffer.cpp) -add_sources(KRAudioManager.cpp) -add_sources(KRAudioSample.cpp) -add_sources(KRAudioSource.cpp) -add_sources(KRBehavior.cpp) -add_sources(KRBone.cpp) -add_sources(KRBundle.cpp) -add_sources(KRBundleManager.cpp) -add_sources(KRCamera.cpp) -add_sources(KRCollider.cpp) -add_sources(KRContext.cpp) -IF(APPLE) - add_sources(KREngine.mm) - add_sources(KRStreamer.mm) - IF(IOS) - add_sources(KRContext_ios.mm) - ELSE() - add_sources(KRContext_osx.mm) - ENDIF() -ENDIF (APPLE) -add_sources(KRContextObject.cpp) -add_sources(KRDataBlock.cpp) -add_sources(KRDirectionalLight.cpp) -IF(APPLE) - add_sources(KRDSP_vDSP.cpp) -ELSE() - add_sources(KRDSP_slow.cpp) -ENDIF() -add_sources(KRHelpers.cpp) -add_sources(KRLight.cpp) -add_sources(KRLocator.cpp) -add_sources(KRLODGroup.cpp) -add_sources(KRLODSet.cpp) -add_sources(KRMaterial.cpp) -add_sources(KRMaterialManager.cpp) -add_sources(KRMesh.cpp) -add_sources(KRMeshCube.cpp) -add_sources(KRMeshManager.cpp) -add_sources(KRMeshQuad.cpp) -add_sources(KRMeshSphere.cpp) -add_sources(KRModel.cpp) -add_sources(KRNode.cpp) -add_sources(KROctree.cpp) -add_sources(KROctreeNode.cpp) -add_sources(KRParticleSystem.cpp) -add_sources(KRParticleSystemNewtonian.h) -add_sources(KRPointLight.cpp) -add_sources(KRRenderSettings.cpp) -add_sources(KRResource+blend.cpp) -# add_sources(KRResource+fbx.cpp) # TODO - Locate FBX SDK dependencies -add_sources(KRResource+obj.cpp) -add_sources(KRResource.cpp) -add_sources(KRReverbZone.cpp) -add_sources(KRScene.cpp) -add_sources(KRShader.cpp) -add_sources(KRShaderManager.cpp) -add_sources(KRSpotLight.cpp) -add_sources(KRSprite.cpp) -add_sources(KRTexture.cpp) -add_sources(KRTexture2D.cpp) -add_sources(KRTextureAnimated.cpp) -add_sources(KRTextureCube.cpp) -add_sources(KRTextureKTX.cpp) -add_sources(KRTextureManager.cpp) -add_sources(KRTexturePVR.cpp) -add_sources(KRTextureTGA.cpp) -add_sources(KRUnknown.cpp) -add_sources(KRUnknownManager.cpp) -add_sources(KRViewport.cpp) +include_directories(public) +add_subdirectory(public) +set(KRAKEN_PUBLIC_HEADERS "${KRAKEN_PUBLIC_HEADERS}" PARENT_SCOPE) + +# Private Implementation +add_sources(KRAmbientZone.cpp) +add_sources(KRAnimation.cpp) +add_sources(KRAnimationAttribute.cpp) +add_sources(KRAnimationCurve.cpp) +add_sources(KRAnimationCurveManager.cpp) +add_sources(KRAnimationLayer.cpp) +add_sources(KRAnimationManager.cpp) +add_sources(KRAudioBuffer.cpp) +add_sources(KRAudioManager.cpp) +add_sources(KRAudioSample.cpp) +add_sources(KRAudioSource.cpp) +add_sources(KRBehavior.cpp) +add_sources(KRBone.cpp) +add_sources(KRBundle.cpp) +add_sources(KRBundleManager.cpp) +add_sources(KRCamera.cpp) +add_sources(KRCollider.cpp) +add_sources(KRContext.cpp) +IF(APPLE) + add_sources(KREngine.mm) + add_sources(KRStreamer.mm) + IF(IOS) + add_sources(KRContext_ios.mm) + ELSE() + add_sources(KRContext_osx.mm) + ENDIF() +ENDIF (APPLE) +add_sources(KRContextObject.cpp) +add_sources(KRDataBlock.cpp) +add_sources(KRDirectionalLight.cpp) +IF(APPLE) + add_sources(KRDSP_vDSP.cpp) +ELSE() + add_sources(KRDSP_slow.cpp) +ENDIF() +add_sources(KRHelpers.cpp) +add_sources(KRLight.cpp) +add_sources(KRLocator.cpp) +add_sources(KRLODGroup.cpp) +add_sources(KRLODSet.cpp) +add_sources(KRMaterial.cpp) +add_sources(KRMaterialManager.cpp) +add_sources(KRMesh.cpp) +add_sources(KRMeshCube.cpp) +add_sources(KRMeshManager.cpp) +add_sources(KRMeshQuad.cpp) +add_sources(KRMeshSphere.cpp) +add_sources(KRModel.cpp) +add_sources(KRNode.cpp) +add_sources(KROctree.cpp) +add_sources(KROctreeNode.cpp) +add_sources(KRParticleSystem.cpp) +add_sources(KRParticleSystemNewtonian.h) +add_sources(KRPointLight.cpp) +add_sources(KRRenderSettings.cpp) +add_sources(KRResource+blend.cpp) +# add_sources(KRResource+fbx.cpp) # TODO - Locate FBX SDK dependencies +add_sources(KRResource+obj.cpp) +add_sources(KRResource.cpp) +add_sources(KRReverbZone.cpp) +add_sources(KRScene.cpp) +add_sources(KRShader.cpp) +add_sources(KRShaderManager.cpp) +add_sources(KRSpotLight.cpp) +add_sources(KRSprite.cpp) +add_sources(KRTexture.cpp) +add_sources(KRTexture2D.cpp) +add_sources(KRTextureAnimated.cpp) +add_sources(KRTextureCube.cpp) +add_sources(KRTextureKTX.cpp) +add_sources(KRTextureManager.cpp) +add_sources(KRTexturePVR.cpp) +add_sources(KRTextureTGA.cpp) +add_sources(KRUnknown.cpp) +add_sources(KRUnknownManager.cpp) +add_sources(KRViewport.cpp) diff --git a/kraken/KRAmbientZone.cpp b/kraken/KRAmbientZone.cpp index e956c09..dbec7ab 100755 --- a/kraken/KRAmbientZone.cpp +++ b/kraken/KRAmbientZone.cpp @@ -1,167 +1,167 @@ -// -// KRAmbientZone.cpp -// KREngine -// -// Created by Kearwood Gilbert on 2012-12-06. -// Copyright (c) 2012 Kearwood Software. All rights reserved. -// - -#include "KRAmbientZone.h" -#include "KRContext.h" - -KRAmbientZone::KRAmbientZone(KRScene &scene, std::string name) : KRNode(scene, name) -{ - m_ambient = ""; - m_ambient_gain = 1.0f; - - m_gradient_distance = 0.25f; - -} - -KRAmbientZone::~KRAmbientZone() -{ -} - -std::string KRAmbientZone::getElementName() { - return "ambient_zone"; -} - -tinyxml2::XMLElement *KRAmbientZone::saveXML( tinyxml2::XMLNode *parent) -{ - tinyxml2::XMLElement *e = KRNode::saveXML(parent); - e->SetAttribute("zone", m_zone.c_str()); - e->SetAttribute("sample", m_ambient.c_str()); - e->SetAttribute("gain", m_ambient_gain); - e->SetAttribute("gradient", m_gradient_distance); - return e; -} - -void KRAmbientZone::loadXML(tinyxml2::XMLElement *e) -{ - KRNode::loadXML(e); - - m_zone = e->Attribute("zone"); - - m_gradient_distance = 0.25f; - if(e->QueryFloatAttribute("gradient", &m_gradient_distance) != tinyxml2::XML_SUCCESS) { - m_gradient_distance = 0.25f; - } - - m_ambient = e->Attribute("sample"); - - m_ambient_gain = 1.0f; - if(e->QueryFloatAttribute("gain", &m_ambient_gain) != tinyxml2::XML_SUCCESS) { - m_ambient_gain = 1.0f; - } -} - -std::string KRAmbientZone::getAmbient() -{ - return m_ambient; -} - -void KRAmbientZone::setAmbient(const std::string &ambient) -{ - m_ambient = ambient; -} - -float KRAmbientZone::getAmbientGain() -{ - return m_ambient_gain; -} - -void KRAmbientZone::setAmbientGain(float ambient_gain) -{ - m_ambient_gain = ambient_gain; -} - -std::string KRAmbientZone::getZone() -{ - return m_zone; -} - -void KRAmbientZone::setZone(const std::string &zone) -{ - m_zone = zone; -} - -void KRAmbientZone::render(KRCamera *pCamera, std::vector &point_lights, std::vector &directional_lights, std::vector&spot_lights, const KRViewport &viewport, KRNode::RenderPass renderPass) -{ - if(m_lod_visible <= LOD_VISIBILITY_PRESTREAM) return; - - KRNode::render(pCamera, point_lights, directional_lights, spot_lights, viewport, renderPass); - - bool bVisualize = pCamera->settings.debug_display == KRRenderSettings::KRENGINE_DEBUG_DISPLAY_SIREN_AMBIENT_ZONES; - - if(renderPass == KRNode::RENDER_PASS_FORWARD_TRANSPARENT && bVisualize) { - Matrix4 sphereModelMatrix = getModelMatrix(); - - KRShader *pShader = getContext().getShaderManager()->getShader("visualize_overlay", pCamera, point_lights, directional_lights, spot_lights, 0, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, renderPass); - - if(getContext().getShaderManager()->selectShader(*pCamera, pShader, viewport, sphereModelMatrix, point_lights, directional_lights, spot_lights, 0, renderPass, Vector3::Zero(), 0.0f, Vector4::Zero())) { - - // Enable additive blending - GLDEBUG(glEnable(GL_BLEND)); - GLDEBUG(glBlendFunc(GL_ONE, GL_ONE)); - - - // Disable z-buffer write - GLDEBUG(glDepthMask(GL_FALSE)); - - // Enable z-buffer test - GLDEBUG(glEnable(GL_DEPTH_TEST)); - GLDEBUG(glDepthFunc(GL_LEQUAL)); - GLDEBUG(glDepthRangef(0.0, 1.0)); - std::vector sphereModels = getContext().getMeshManager()->getModel("__sphere"); - if(sphereModels.size()) { - for(int i=0; i < sphereModels[0]->getSubmeshCount(); i++) { - sphereModels[0]->renderSubmesh(i, renderPass, getName(), "visualize_overlay", 1.0f); - } - } - - // Enable alpha blending - GLDEBUG(glEnable(GL_BLEND)); - GLDEBUG(glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA)); - } - } -} - - -float KRAmbientZone::getGradientDistance() -{ - return m_gradient_distance; -} - -void KRAmbientZone::setGradientDistance(float gradient_distance) -{ - m_gradient_distance = gradient_distance; -} - -AABB KRAmbientZone::getBounds() { - // Ambient zones always have a -1, -1, -1 to 1, 1, 1 bounding box - return AABB(-Vector3::One(), Vector3::One(), getModelMatrix()); -} - -float KRAmbientZone::getContainment(const Vector3 &pos) -{ - AABB bounds = getBounds(); - if(bounds.contains(pos)) { - Vector3 size = bounds.size(); - Vector3 diff = pos - bounds.center(); - diff = diff * 2.0f; - diff = Vector3(diff.x / size.x, diff.y / size.y, diff.z / size.z); - float d = diff.magnitude(); - - if(m_gradient_distance <= 0.0f) { - // Avoid division by zero - d = d > 1.0f ? 0.0f : 1.0f; - } else { - d = (1.0f - d) / m_gradient_distance; - d = KRCLAMP(d, 0.0f, 1.0f); - } - return d; - - } else { - return 0.0f; - } +// +// KRAmbientZone.cpp +// KREngine +// +// Created by Kearwood Gilbert on 2012-12-06. +// Copyright (c) 2012 Kearwood Software. All rights reserved. +// + +#include "KRAmbientZone.h" +#include "KRContext.h" + +KRAmbientZone::KRAmbientZone(KRScene &scene, std::string name) : KRNode(scene, name) +{ + m_ambient = ""; + m_ambient_gain = 1.0f; + + m_gradient_distance = 0.25f; + +} + +KRAmbientZone::~KRAmbientZone() +{ +} + +std::string KRAmbientZone::getElementName() { + return "ambient_zone"; +} + +tinyxml2::XMLElement *KRAmbientZone::saveXML( tinyxml2::XMLNode *parent) +{ + tinyxml2::XMLElement *e = KRNode::saveXML(parent); + e->SetAttribute("zone", m_zone.c_str()); + e->SetAttribute("sample", m_ambient.c_str()); + e->SetAttribute("gain", m_ambient_gain); + e->SetAttribute("gradient", m_gradient_distance); + return e; +} + +void KRAmbientZone::loadXML(tinyxml2::XMLElement *e) +{ + KRNode::loadXML(e); + + m_zone = e->Attribute("zone"); + + m_gradient_distance = 0.25f; + if(e->QueryFloatAttribute("gradient", &m_gradient_distance) != tinyxml2::XML_SUCCESS) { + m_gradient_distance = 0.25f; + } + + m_ambient = e->Attribute("sample"); + + m_ambient_gain = 1.0f; + if(e->QueryFloatAttribute("gain", &m_ambient_gain) != tinyxml2::XML_SUCCESS) { + m_ambient_gain = 1.0f; + } +} + +std::string KRAmbientZone::getAmbient() +{ + return m_ambient; +} + +void KRAmbientZone::setAmbient(const std::string &ambient) +{ + m_ambient = ambient; +} + +float KRAmbientZone::getAmbientGain() +{ + return m_ambient_gain; +} + +void KRAmbientZone::setAmbientGain(float ambient_gain) +{ + m_ambient_gain = ambient_gain; +} + +std::string KRAmbientZone::getZone() +{ + return m_zone; +} + +void KRAmbientZone::setZone(const std::string &zone) +{ + m_zone = zone; +} + +void KRAmbientZone::render(KRCamera *pCamera, std::vector &point_lights, std::vector &directional_lights, std::vector&spot_lights, const KRViewport &viewport, KRNode::RenderPass renderPass) +{ + if(m_lod_visible <= LOD_VISIBILITY_PRESTREAM) return; + + KRNode::render(pCamera, point_lights, directional_lights, spot_lights, viewport, renderPass); + + bool bVisualize = pCamera->settings.debug_display == KRRenderSettings::KRENGINE_DEBUG_DISPLAY_SIREN_AMBIENT_ZONES; + + if(renderPass == KRNode::RENDER_PASS_FORWARD_TRANSPARENT && bVisualize) { + Matrix4 sphereModelMatrix = getModelMatrix(); + + KRShader *pShader = getContext().getShaderManager()->getShader("visualize_overlay", pCamera, point_lights, directional_lights, spot_lights, 0, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, renderPass); + + if(getContext().getShaderManager()->selectShader(*pCamera, pShader, viewport, sphereModelMatrix, point_lights, directional_lights, spot_lights, 0, renderPass, Vector3::Zero(), 0.0f, Vector4::Zero())) { + + // Enable additive blending + GLDEBUG(glEnable(GL_BLEND)); + GLDEBUG(glBlendFunc(GL_ONE, GL_ONE)); + + + // Disable z-buffer write + GLDEBUG(glDepthMask(GL_FALSE)); + + // Enable z-buffer test + GLDEBUG(glEnable(GL_DEPTH_TEST)); + GLDEBUG(glDepthFunc(GL_LEQUAL)); + GLDEBUG(glDepthRangef(0.0, 1.0)); + std::vector sphereModels = getContext().getMeshManager()->getModel("__sphere"); + if(sphereModels.size()) { + for(int i=0; i < sphereModels[0]->getSubmeshCount(); i++) { + sphereModels[0]->renderSubmesh(i, renderPass, getName(), "visualize_overlay", 1.0f); + } + } + + // Enable alpha blending + GLDEBUG(glEnable(GL_BLEND)); + GLDEBUG(glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA)); + } + } +} + + +float KRAmbientZone::getGradientDistance() +{ + return m_gradient_distance; +} + +void KRAmbientZone::setGradientDistance(float gradient_distance) +{ + m_gradient_distance = gradient_distance; +} + +AABB KRAmbientZone::getBounds() { + // Ambient zones always have a -1, -1, -1 to 1, 1, 1 bounding box + return AABB::Create(-Vector3::One(), Vector3::One(), getModelMatrix()); +} + +float KRAmbientZone::getContainment(const Vector3 &pos) +{ + AABB bounds = getBounds(); + if(bounds.contains(pos)) { + Vector3 size = bounds.size(); + Vector3 diff = pos - bounds.center(); + diff = diff * 2.0f; + diff = Vector3::Create(diff.x / size.x, diff.y / size.y, diff.z / size.z); + float d = diff.magnitude(); + + if(m_gradient_distance <= 0.0f) { + // Avoid division by zero + d = d > 1.0f ? 0.0f : 1.0f; + } else { + d = (1.0f - d) / m_gradient_distance; + d = KRCLAMP(d, 0.0f, 1.0f); + } + return d; + + } else { + return 0.0f; + } } \ No newline at end of file diff --git a/kraken/KRAudioManager.cpp b/kraken/KRAudioManager.cpp index 8634529..0d1d1cd 100755 --- a/kraken/KRAudioManager.cpp +++ b/kraken/KRAudioManager.cpp @@ -1,1926 +1,1926 @@ -// -// FileManager.cpp -// KREngine -// -// Copyright 2012 Kearwood Gilbert. All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, are -// permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of -// conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list -// of conditions and the following disclaimer in the documentation and/or other materials -// provided with the distribution. -// -// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED -// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR -// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// The views and conclusions contained in the software and documentation are those of the -// authors and should not be interpreted as representing official policies, either expressed -// or implied, of Kearwood Gilbert. -// - -#include "KRAudioManager.h" -#include "KRAudioSample.h" -#include "KREngine-common.h" -#include "KRDataBlock.h" -#include "KRAudioBuffer.h" -#include "KRContext.h" -#include "KRCollider.h" -#include "KRDSP.h" - -KRAudioManager::KRAudioManager(KRContext &context) - : KRContextObject(context) - , m_initialized(false) -{ - m_enable_audio = true; - m_enable_hrtf = true; - m_enable_reverb = true; - m_reverb_max_length = 8.0f; - - m_anticlick_block = true; -#ifdef __APPLE__ - mach_timebase_info(&m_timebase_info); -#endif - - m_high_quality_hrtf = false; - - m_listener_scene = NULL; - - m_global_gain = 0.20f; - m_global_reverb_send_level = 1.0f; - m_global_ambient_gain = 1.0f; - -#ifdef __APPLE__ - // Apple Core Audio - m_auGraph = NULL; - m_auMixer = NULL; -#endif - - m_audio_frame = 0; - - m_output_sample = 0; - - - m_reverb_input_samples = NULL; - m_reverb_input_next_sample = 0; - - m_workspace_data = NULL; - m_reverb_sequence = 0; - - m_hrtf_data = NULL; - - for(int i=0; i < KRENGINE_MAX_REVERB_IMPULSE_MIX; i++) { - m_reverb_impulse_responses[i] = NULL; - m_reverb_impulse_responses_weight[i] = 0.0f; - } - m_output_accumulation = NULL; -} - -unordered_map &KRAudioManager::getSounds() -{ - return m_sounds; -} - -bool KRAudioManager::getEnableAudio() -{ - return m_enable_audio; -} -void KRAudioManager::setEnableAudio(bool enable) -{ - m_enable_audio = enable; -} - -bool KRAudioManager::getEnableHRTF() -{ - return m_enable_hrtf; -} -void KRAudioManager::setEnableHRTF(bool enable) -{ - m_enable_hrtf = enable; -} - -bool KRAudioManager::getEnableReverb() -{ - return m_enable_reverb; -} - -float KRAudioManager::getReverbMaxLength() -{ - return m_reverb_max_length; -} - -void KRAudioManager::setEnableReverb(bool enable) -{ - m_enable_reverb = enable; -} - -void KRAudioManager::setReverbMaxLength(float max_length) -{ - m_reverb_max_length = max_length; -} - -KRScene *KRAudioManager::getListenerScene() -{ - return m_listener_scene; -} - -void KRAudioManager::setListenerScene(KRScene *scene) -{ - m_listener_scene = scene; -} - -#ifdef __APPLE__ -// Apple Core Audio -void KRAudioManager::renderAudio(UInt32 inNumberFrames, AudioBufferList *ioData) -{ - // uint64_t start_time = mach_absolute_time(); - Float32 *outA = (Float32 *)ioData->mBuffers[0].mData; - Float32 *outB = (Float32 *)ioData->mBuffers[1].mData; // Non-Interleaved only - - int output_frame = 0; - - while(output_frame < inNumberFrames) { - int frames_ready = KRENGINE_AUDIO_BLOCK_LENGTH - m_output_sample; - if(frames_ready == 0) { - renderBlock(); - m_output_sample = 0; - frames_ready = KRENGINE_AUDIO_BLOCK_LENGTH; - } - - int frames_processed = inNumberFrames - output_frame; - if(frames_processed > frames_ready) frames_processed = frames_ready; - - float *block_data = getBlockAddress(0); - - for(int i=0; i < frames_processed; i++) { - - float left_channel = block_data[m_output_sample * KRENGINE_MAX_OUTPUT_CHANNELS]; - float right_channel = block_data[m_output_sample * KRENGINE_MAX_OUTPUT_CHANNELS + 1]; - m_output_sample++; - - // Interleaved - // outA[i*2] = (Float32)left_channel; - // outA[i*2 + 1] = (Float32)right_channel; - - // Non-Interleaved - outA[output_frame] = (Float32)left_channel; - outB[output_frame] = (Float32)right_channel; - output_frame++; - } - } - -// uint64_t end_time = mach_absolute_time(); -// uint64_t duration = (end_time - start_time) * m_timebase_info.numer / m_timebase_info.denom; // Nanoseconds -// double ms = duration; -// ms = ms / 1000000.0; -// uint64_t max_duration = (uint64_t)inNumberFrames * 1000000000 / 44100; -// fprintf(stderr, "audio load: %5.1f%% hrtf channels: %li\n", (float)(duration * 1000 / max_duration) / 10.0f, m_mapped_sources.size()); -// printf("ms %2.3f frames %ld audio load: %5.1f%% hrtf channels: %li\n", ms, (unsigned long) inNumberFrames, (float)(duration * 1000 / max_duration) / 10.0f, m_mapped_sources.size()); -} -#endif - -float *KRAudioManager::getBlockAddress(int block_offset) -{ - return m_output_accumulation + (m_output_accumulation_block_start + block_offset * KRENGINE_AUDIO_BLOCK_LENGTH * KRENGINE_MAX_OUTPUT_CHANNELS) % (KRENGINE_REVERB_MAX_SAMPLES * KRENGINE_MAX_OUTPUT_CHANNELS); -} - -void KRAudioManager::renderReverbImpulseResponse(int impulse_response_offset, int frame_count_log2) -{ - int frame_count = 1 << frame_count_log2; - int fft_size = frame_count * 2; - int fft_size_log2 = frame_count_log2 + 1; - - KRDSP::SplitComplex reverb_sample_data_complex = m_workspace[0]; - KRDSP::SplitComplex impulse_block_data_complex = m_workspace[1]; - KRDSP::SplitComplex conv_data_complex = m_workspace[2]; - - int reverb_offset = (m_reverb_input_next_sample + KRENGINE_AUDIO_BLOCK_LENGTH - frame_count); - if(reverb_offset < 0) { - reverb_offset += KRENGINE_REVERB_MAX_SAMPLES; - } else { - reverb_offset = reverb_offset % KRENGINE_REVERB_MAX_SAMPLES; - } - - int frames_left = frame_count; - while(frames_left) { - int frames_to_process = KRENGINE_REVERB_MAX_SAMPLES - reverb_offset; - if(frames_to_process > frames_left) frames_to_process = frames_left; - memcpy(reverb_sample_data_complex.realp + frame_count - frames_left, m_reverb_input_samples + reverb_offset, frames_to_process * sizeof(float)); - - frames_left -= frames_to_process; - reverb_offset = (reverb_offset + frames_to_process) % KRENGINE_REVERB_MAX_SAMPLES; - } - - memset(reverb_sample_data_complex.realp + frame_count, 0, frame_count * sizeof(float)); - memset(reverb_sample_data_complex.imagp, 0, fft_size * sizeof(float)); - - KRDSP::FFTForward(m_fft_setup[fft_size_log2 - KRENGINE_AUDIO_BLOCK_LOG2N], &reverb_sample_data_complex, fft_size_log2); - - float scale = 0.5f / fft_size; - - int impulse_response_channels = 2; - for(int channel=0; channel < impulse_response_channels; channel++) { - bool first_sample = true; - for(unordered_map::iterator zone_itr=m_reverb_zone_weights.begin(); zone_itr != m_reverb_zone_weights.end(); zone_itr++) { - siren_reverb_zone_weight_info zi = (*zone_itr).second; - if(zi.reverb_sample) { - if(impulse_response_offset < KRMIN(zi.reverb_sample->getFrameCount(), m_reverb_max_length * 44100)) { // Optimization - when mixing multiple impulse responses (i.e. fading between reverb zones), do not process blocks past the end of a shorter impulse response sample when they differ in length - if(first_sample) { - // If this is the first or only sample, write directly to the first half of the FFT input buffer - first_sample = false; - zi.reverb_sample->sample(impulse_response_offset, frame_count, channel, impulse_block_data_complex.realp, zi.weight, false); - } else { - // Subsequent samples write to the second half of the FFT input buffer, which is then added to the first half (the second half will be zero'ed out anyways and works as a convenient temporary buffer) - zi.reverb_sample->sample(impulse_response_offset, frame_count, channel, impulse_block_data_complex.realp + frame_count, zi.weight, false); - KRDSP::Accumulate(impulse_block_data_complex.realp, 1, impulse_block_data_complex.realp + frame_count, 1, frame_count); - } - } - - } - } - memset(impulse_block_data_complex.realp + frame_count, 0, frame_count * sizeof(float)); - memset(impulse_block_data_complex.imagp, 0, fft_size * sizeof(float)); - - - - KRDSP::FFTForward(m_fft_setup[fft_size_log2 - KRENGINE_AUDIO_BLOCK_LOG2N], &impulse_block_data_complex, fft_size_log2); - KRDSP::Multiply(&reverb_sample_data_complex, &impulse_block_data_complex, &conv_data_complex, fft_size); - KRDSP::FFTInverse(m_fft_setup[fft_size_log2 - KRENGINE_AUDIO_BLOCK_LOG2N], &conv_data_complex, fft_size_log2); - KRDSP::Scale(conv_data_complex.realp, scale, fft_size); - - - int output_offset = (m_output_accumulation_block_start + impulse_response_offset * KRENGINE_MAX_OUTPUT_CHANNELS) % (KRENGINE_REVERB_MAX_SAMPLES * KRENGINE_MAX_OUTPUT_CHANNELS); - frames_left = fft_size; - while(frames_left) { - int frames_to_process = (KRENGINE_REVERB_MAX_SAMPLES * KRENGINE_MAX_OUTPUT_CHANNELS - output_offset) / KRENGINE_MAX_OUTPUT_CHANNELS; - if(frames_to_process > frames_left) frames_to_process = frames_left; - KRDSP::Accumulate(m_output_accumulation + output_offset + channel, KRENGINE_MAX_OUTPUT_CHANNELS, - conv_data_complex.realp + fft_size - frames_left, 1, - frames_to_process); - frames_left -= frames_to_process; - output_offset = (output_offset + frames_to_process * KRENGINE_MAX_OUTPUT_CHANNELS) % (KRENGINE_REVERB_MAX_SAMPLES * KRENGINE_MAX_OUTPUT_CHANNELS); - } - } -} - -void KRAudioManager::renderReverb() -{ - float reverb_data[KRENGINE_AUDIO_BLOCK_LENGTH]; - - float *reverb_accum = m_reverb_input_samples + m_reverb_input_next_sample; - memset(reverb_accum, 0, sizeof(float) * KRENGINE_AUDIO_BLOCK_LENGTH); - - std::set active_sources = m_activeAudioSources; - - for(std::set::iterator itr=active_sources.begin(); itr != active_sources.end(); itr++) { - KRAudioSource *source = *itr; - if(&source->getScene() == m_listener_scene) { - float containment_factor = 0.0f; - - for(unordered_map::iterator zone_itr=m_reverb_zone_weights.begin(); zone_itr != m_reverb_zone_weights.end(); zone_itr++) { - siren_reverb_zone_weight_info zi = (*zone_itr).second; - float gain = zi.weight * zi.reverb_zone->getReverbGain() * zi.reverb_zone->getContainment(source->getWorldTranslation()); - if(gain > containment_factor) containment_factor = gain; - } - - - float reverb_send_level = m_global_reverb_send_level * m_global_gain * source->getReverb() * containment_factor; - if(reverb_send_level > 0.0f) { - source->sample(KRENGINE_AUDIO_BLOCK_LENGTH, 0, reverb_data, reverb_send_level); - KRDSP::Accumulate(reverb_accum, 1, reverb_data, 1, KRENGINE_AUDIO_BLOCK_LENGTH); - } - } - } - - // Apply impulse response reverb - // KRAudioSample *impulse_response = getContext().getAudioManager()->get("hrtf_kemar_H10e040a"); - - int impulse_response_blocks = 0; - for(unordered_map::iterator zone_itr=m_reverb_zone_weights.begin(); zone_itr != m_reverb_zone_weights.end(); zone_itr++) { - siren_reverb_zone_weight_info zi = (*zone_itr).second; - if(zi.reverb_sample) { - int zone_sample_blocks = KRMIN(zi.reverb_sample->getFrameCount(), m_reverb_max_length * 44100) / KRENGINE_AUDIO_BLOCK_LENGTH + 1; - impulse_response_blocks = KRMAX(impulse_response_blocks, zone_sample_blocks); - } - } - -// KRAudioSample *impulse_response_sample = getContext().getAudioManager()->get("test_reverb"); - if(impulse_response_blocks) { -// int impulse_response_blocks = impulse_response_sample->getFrameCount() / KRENGINE_AUDIO_BLOCK_LENGTH + 1; - int period_log2 = 0; - int impulse_response_block=0; - int period_count = 0; - - while(impulse_response_block < impulse_response_blocks) { - int period = 1 << period_log2; - - if((m_reverb_sequence + period - period_count) % period == 0) { - renderReverbImpulseResponse(impulse_response_block * KRENGINE_AUDIO_BLOCK_LENGTH, KRENGINE_AUDIO_BLOCK_LOG2N + period_log2); - } - - impulse_response_block += period; - - period_count++; - if(period_count >= period) { - period_count = 0; - if(KRENGINE_AUDIO_BLOCK_LOG2N + period_log2 + 1 < KRENGINE_REVERB_MAX_FFT_LOG2) { // FFT Size is double the number of frames in the period - period_log2++; - } - } - } - } - - m_reverb_sequence = (m_reverb_sequence + 1) % 0x1000000; - - // Rotate reverb buffer - m_reverb_input_next_sample = (m_reverb_input_next_sample + KRENGINE_AUDIO_BLOCK_LENGTH) % KRENGINE_REVERB_MAX_SAMPLES; -} - -void KRAudioManager::renderBlock() -{ - m_mutex.lock(); - - // ----====---- Advance to next block in accumulation buffer ----====---- - - // Zero out block that was last used, so it will be ready for the next pass through the circular buffer - float *block_data = getBlockAddress(0); - memset(block_data, 0, KRENGINE_AUDIO_BLOCK_LENGTH * KRENGINE_MAX_OUTPUT_CHANNELS * sizeof(float)); - - // Advance to the next block, and wrap around - m_output_accumulation_block_start = (m_output_accumulation_block_start + KRENGINE_AUDIO_BLOCK_LENGTH * KRENGINE_MAX_OUTPUT_CHANNELS) % (KRENGINE_REVERB_MAX_SAMPLES * KRENGINE_MAX_OUTPUT_CHANNELS); - - if(m_enable_audio) { - // ----====---- Render Direct / HRTF audio ----====---- - if(m_enable_hrtf) { - renderHRTF(); - } else { - renderITD(); - } - - // ----====---- Render Indirect / Reverb channel ----====---- - if(m_enable_reverb && m_reverb_max_length > 0.0f) { - renderReverb(); - } - - // ----====---- Render Ambient Sound ----====---- - renderAmbient(); - - // ----====---- Render Ambient Sound ----====---- - renderLimiter(); - } - - // ----====---- Advance audio sources ----====---- - m_audio_frame += KRENGINE_AUDIO_BLOCK_LENGTH; - - std::set open_samples = m_openAudioSamples; - - for(auto itr=open_samples.begin(); itr != open_samples.end(); itr++) { - KRAudioSample *sample = *itr; - sample->_endFrame(); - } - - m_anticlick_block = false; - m_mutex.unlock(); -} - -#ifdef __APPLE__ -// Apple Core Audio - -// audio render procedure, don't allocate memory, don't take any locks, don't waste time -OSStatus KRAudioManager::renderInput(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData) -{ - ((KRAudioManager*)inRefCon)->renderAudio(inNumberFrames, ioData); - return noErr; -} - -void KRSetAUCanonical(AudioStreamBasicDescription &desc, UInt32 nChannels, bool interleaved) -{ - desc.mFormatID = kAudioFormatLinearPCM; - desc.mFormatFlags = kAudioFormatFlagsNativeFloatPacked; - desc.mChannelsPerFrame = nChannels; - desc.mFramesPerPacket = 1; - desc.mBitsPerChannel = 8 * sizeof(Float32); - if (interleaved) - desc.mBytesPerPacket = desc.mBytesPerFrame = nChannels * sizeof(Float32); - else { - desc.mBytesPerPacket = desc.mBytesPerFrame = sizeof(Float32); - desc.mFormatFlags |= kAudioFormatFlagIsNonInterleaved; - } -} -#endif // Apple Core Audio - -void KRAudioManager::initHRTF() -{ - m_hrtf_sample_locations.push_back(Vector2(-10.0f,000.0f)); - m_hrtf_sample_locations.push_back(Vector2(-10.0f,005.0f)); - m_hrtf_sample_locations.push_back(Vector2(-10.0f,010.0f)); - m_hrtf_sample_locations.push_back(Vector2(-10.0f,015.0f)); - m_hrtf_sample_locations.push_back(Vector2(-10.0f,020.0f)); - m_hrtf_sample_locations.push_back(Vector2(-10.0f,025.0f)); - m_hrtf_sample_locations.push_back(Vector2(-10.0f,030.0f)); - m_hrtf_sample_locations.push_back(Vector2(-10.0f,035.0f)); - m_hrtf_sample_locations.push_back(Vector2(-10.0f,040.0f)); - m_hrtf_sample_locations.push_back(Vector2(-10.0f,045.0f)); - m_hrtf_sample_locations.push_back(Vector2(-10.0f,050.0f)); - m_hrtf_sample_locations.push_back(Vector2(-10.0f,055.0f)); - m_hrtf_sample_locations.push_back(Vector2(-10.0f,060.0f)); - m_hrtf_sample_locations.push_back(Vector2(-10.0f,065.0f)); - m_hrtf_sample_locations.push_back(Vector2(-10.0f,070.0f)); - m_hrtf_sample_locations.push_back(Vector2(-10.0f,075.0f)); - m_hrtf_sample_locations.push_back(Vector2(-10.0f,080.0f)); - m_hrtf_sample_locations.push_back(Vector2(-10.0f,085.0f)); - m_hrtf_sample_locations.push_back(Vector2(-10.0f,090.0f)); - m_hrtf_sample_locations.push_back(Vector2(-10.0f,095.0f)); - m_hrtf_sample_locations.push_back(Vector2(-10.0f,100.0f)); - m_hrtf_sample_locations.push_back(Vector2(-10.0f,105.0f)); - m_hrtf_sample_locations.push_back(Vector2(-10.0f,110.0f)); - m_hrtf_sample_locations.push_back(Vector2(-10.0f,115.0f)); - m_hrtf_sample_locations.push_back(Vector2(-10.0f,120.0f)); - m_hrtf_sample_locations.push_back(Vector2(-10.0f,125.0f)); - m_hrtf_sample_locations.push_back(Vector2(-10.0f,130.0f)); - m_hrtf_sample_locations.push_back(Vector2(-10.0f,135.0f)); - m_hrtf_sample_locations.push_back(Vector2(-10.0f,140.0f)); - m_hrtf_sample_locations.push_back(Vector2(-10.0f,145.0f)); - m_hrtf_sample_locations.push_back(Vector2(-10.0f,150.0f)); - m_hrtf_sample_locations.push_back(Vector2(-10.0f,155.0f)); - m_hrtf_sample_locations.push_back(Vector2(-10.0f,160.0f)); - m_hrtf_sample_locations.push_back(Vector2(-10.0f,165.0f)); - m_hrtf_sample_locations.push_back(Vector2(-10.0f,170.0f)); - m_hrtf_sample_locations.push_back(Vector2(-10.0f,175.0f)); - m_hrtf_sample_locations.push_back(Vector2(-10.0f,180.0f)); - m_hrtf_sample_locations.push_back(Vector2(-20.0f,000.0f)); - m_hrtf_sample_locations.push_back(Vector2(-20.0f,005.0f)); - m_hrtf_sample_locations.push_back(Vector2(-20.0f,010.0f)); - m_hrtf_sample_locations.push_back(Vector2(-20.0f,015.0f)); - m_hrtf_sample_locations.push_back(Vector2(-20.0f,020.0f)); - m_hrtf_sample_locations.push_back(Vector2(-20.0f,025.0f)); - m_hrtf_sample_locations.push_back(Vector2(-20.0f,030.0f)); - m_hrtf_sample_locations.push_back(Vector2(-20.0f,035.0f)); - m_hrtf_sample_locations.push_back(Vector2(-20.0f,040.0f)); - m_hrtf_sample_locations.push_back(Vector2(-20.0f,045.0f)); - m_hrtf_sample_locations.push_back(Vector2(-20.0f,050.0f)); - m_hrtf_sample_locations.push_back(Vector2(-20.0f,055.0f)); - m_hrtf_sample_locations.push_back(Vector2(-20.0f,060.0f)); - m_hrtf_sample_locations.push_back(Vector2(-20.0f,065.0f)); - m_hrtf_sample_locations.push_back(Vector2(-20.0f,070.0f)); - m_hrtf_sample_locations.push_back(Vector2(-20.0f,075.0f)); - m_hrtf_sample_locations.push_back(Vector2(-20.0f,080.0f)); - m_hrtf_sample_locations.push_back(Vector2(-20.0f,085.0f)); - m_hrtf_sample_locations.push_back(Vector2(-20.0f,090.0f)); - m_hrtf_sample_locations.push_back(Vector2(-20.0f,095.0f)); - m_hrtf_sample_locations.push_back(Vector2(-20.0f,100.0f)); - m_hrtf_sample_locations.push_back(Vector2(-20.0f,105.0f)); - m_hrtf_sample_locations.push_back(Vector2(-20.0f,110.0f)); - m_hrtf_sample_locations.push_back(Vector2(-20.0f,115.0f)); - m_hrtf_sample_locations.push_back(Vector2(-20.0f,120.0f)); - m_hrtf_sample_locations.push_back(Vector2(-20.0f,125.0f)); - m_hrtf_sample_locations.push_back(Vector2(-20.0f,130.0f)); - m_hrtf_sample_locations.push_back(Vector2(-20.0f,135.0f)); - m_hrtf_sample_locations.push_back(Vector2(-20.0f,140.0f)); - m_hrtf_sample_locations.push_back(Vector2(-20.0f,145.0f)); - m_hrtf_sample_locations.push_back(Vector2(-20.0f,150.0f)); - m_hrtf_sample_locations.push_back(Vector2(-20.0f,155.0f)); - m_hrtf_sample_locations.push_back(Vector2(-20.0f,160.0f)); - m_hrtf_sample_locations.push_back(Vector2(-20.0f,165.0f)); - m_hrtf_sample_locations.push_back(Vector2(-20.0f,170.0f)); - m_hrtf_sample_locations.push_back(Vector2(-20.0f,175.0f)); - m_hrtf_sample_locations.push_back(Vector2(-20.0f,180.0f)); - m_hrtf_sample_locations.push_back(Vector2(-30.0f,000.0f)); - m_hrtf_sample_locations.push_back(Vector2(-30.0f,006.0f)); - m_hrtf_sample_locations.push_back(Vector2(-30.0f,012.0f)); - m_hrtf_sample_locations.push_back(Vector2(-30.0f,018.0f)); - m_hrtf_sample_locations.push_back(Vector2(-30.0f,024.0f)); - m_hrtf_sample_locations.push_back(Vector2(-30.0f,030.0f)); - m_hrtf_sample_locations.push_back(Vector2(-30.0f,036.0f)); - m_hrtf_sample_locations.push_back(Vector2(-30.0f,042.0f)); - m_hrtf_sample_locations.push_back(Vector2(-30.0f,048.0f)); - m_hrtf_sample_locations.push_back(Vector2(-30.0f,054.0f)); - m_hrtf_sample_locations.push_back(Vector2(-30.0f,060.0f)); - m_hrtf_sample_locations.push_back(Vector2(-30.0f,066.0f)); - m_hrtf_sample_locations.push_back(Vector2(-30.0f,072.0f)); - m_hrtf_sample_locations.push_back(Vector2(-30.0f,078.0f)); - m_hrtf_sample_locations.push_back(Vector2(-30.0f,084.0f)); - m_hrtf_sample_locations.push_back(Vector2(-30.0f,090.0f)); - m_hrtf_sample_locations.push_back(Vector2(-30.0f,096.0f)); - m_hrtf_sample_locations.push_back(Vector2(-30.0f,102.0f)); - m_hrtf_sample_locations.push_back(Vector2(-30.0f,108.0f)); - m_hrtf_sample_locations.push_back(Vector2(-30.0f,114.0f)); - m_hrtf_sample_locations.push_back(Vector2(-30.0f,120.0f)); - m_hrtf_sample_locations.push_back(Vector2(-30.0f,126.0f)); - m_hrtf_sample_locations.push_back(Vector2(-30.0f,132.0f)); - m_hrtf_sample_locations.push_back(Vector2(-30.0f,138.0f)); - m_hrtf_sample_locations.push_back(Vector2(-30.0f,144.0f)); - m_hrtf_sample_locations.push_back(Vector2(-30.0f,150.0f)); - m_hrtf_sample_locations.push_back(Vector2(-30.0f,156.0f)); - m_hrtf_sample_locations.push_back(Vector2(-30.0f,162.0f)); - m_hrtf_sample_locations.push_back(Vector2(-30.0f,168.0f)); - m_hrtf_sample_locations.push_back(Vector2(-30.0f,174.0f)); - m_hrtf_sample_locations.push_back(Vector2(-30.0f,180.0f)); - m_hrtf_sample_locations.push_back(Vector2(-40.0f,000.0f)); - m_hrtf_sample_locations.push_back(Vector2(-40.0f,006.0f)); - m_hrtf_sample_locations.push_back(Vector2(-40.0f,013.0f)); - m_hrtf_sample_locations.push_back(Vector2(-40.0f,019.0f)); - m_hrtf_sample_locations.push_back(Vector2(-40.0f,026.0f)); - m_hrtf_sample_locations.push_back(Vector2(-40.0f,032.0f)); - m_hrtf_sample_locations.push_back(Vector2(-40.0f,039.0f)); - m_hrtf_sample_locations.push_back(Vector2(-40.0f,045.0f)); - m_hrtf_sample_locations.push_back(Vector2(-40.0f,051.0f)); - m_hrtf_sample_locations.push_back(Vector2(-40.0f,058.0f)); - m_hrtf_sample_locations.push_back(Vector2(-40.0f,064.0f)); - m_hrtf_sample_locations.push_back(Vector2(-40.0f,071.0f)); - m_hrtf_sample_locations.push_back(Vector2(-40.0f,077.0f)); - m_hrtf_sample_locations.push_back(Vector2(-40.0f,084.0f)); - m_hrtf_sample_locations.push_back(Vector2(-40.0f,090.0f)); - m_hrtf_sample_locations.push_back(Vector2(-40.0f,096.0f)); - m_hrtf_sample_locations.push_back(Vector2(-40.0f,103.0f)); - m_hrtf_sample_locations.push_back(Vector2(-40.0f,109.0f)); - m_hrtf_sample_locations.push_back(Vector2(-40.0f,116.0f)); - m_hrtf_sample_locations.push_back(Vector2(-40.0f,122.0f)); - m_hrtf_sample_locations.push_back(Vector2(-40.0f,129.0f)); - m_hrtf_sample_locations.push_back(Vector2(-40.0f,135.0f)); - m_hrtf_sample_locations.push_back(Vector2(-40.0f,141.0f)); - m_hrtf_sample_locations.push_back(Vector2(-40.0f,148.0f)); - m_hrtf_sample_locations.push_back(Vector2(-40.0f,154.0f)); - m_hrtf_sample_locations.push_back(Vector2(-40.0f,161.0f)); - m_hrtf_sample_locations.push_back(Vector2(-40.0f,167.0f)); - m_hrtf_sample_locations.push_back(Vector2(-40.0f,174.0f)); - m_hrtf_sample_locations.push_back(Vector2(-40.0f,180.0f)); - m_hrtf_sample_locations.push_back(Vector2(0.0f,000.0f)); - m_hrtf_sample_locations.push_back(Vector2(0.0f,005.0f)); - m_hrtf_sample_locations.push_back(Vector2(0.0f,010.0f)); - m_hrtf_sample_locations.push_back(Vector2(0.0f,015.0f)); - m_hrtf_sample_locations.push_back(Vector2(0.0f,020.0f)); - m_hrtf_sample_locations.push_back(Vector2(0.0f,025.0f)); - m_hrtf_sample_locations.push_back(Vector2(0.0f,030.0f)); - m_hrtf_sample_locations.push_back(Vector2(0.0f,035.0f)); - m_hrtf_sample_locations.push_back(Vector2(0.0f,040.0f)); - m_hrtf_sample_locations.push_back(Vector2(0.0f,045.0f)); - m_hrtf_sample_locations.push_back(Vector2(0.0f,050.0f)); - m_hrtf_sample_locations.push_back(Vector2(0.0f,055.0f)); - m_hrtf_sample_locations.push_back(Vector2(0.0f,060.0f)); - m_hrtf_sample_locations.push_back(Vector2(0.0f,065.0f)); - m_hrtf_sample_locations.push_back(Vector2(0.0f,070.0f)); - m_hrtf_sample_locations.push_back(Vector2(0.0f,075.0f)); - m_hrtf_sample_locations.push_back(Vector2(0.0f,080.0f)); - m_hrtf_sample_locations.push_back(Vector2(0.0f,085.0f)); - m_hrtf_sample_locations.push_back(Vector2(0.0f,090.0f)); - m_hrtf_sample_locations.push_back(Vector2(0.0f,095.0f)); - m_hrtf_sample_locations.push_back(Vector2(0.0f,100.0f)); - m_hrtf_sample_locations.push_back(Vector2(0.0f,105.0f)); - m_hrtf_sample_locations.push_back(Vector2(0.0f,110.0f)); - m_hrtf_sample_locations.push_back(Vector2(0.0f,115.0f)); - m_hrtf_sample_locations.push_back(Vector2(0.0f,120.0f)); - m_hrtf_sample_locations.push_back(Vector2(0.0f,125.0f)); - m_hrtf_sample_locations.push_back(Vector2(0.0f,130.0f)); - m_hrtf_sample_locations.push_back(Vector2(0.0f,135.0f)); - m_hrtf_sample_locations.push_back(Vector2(0.0f,140.0f)); - m_hrtf_sample_locations.push_back(Vector2(0.0f,145.0f)); - m_hrtf_sample_locations.push_back(Vector2(0.0f,150.0f)); - m_hrtf_sample_locations.push_back(Vector2(0.0f,155.0f)); - m_hrtf_sample_locations.push_back(Vector2(0.0f,160.0f)); - m_hrtf_sample_locations.push_back(Vector2(0.0f,165.0f)); - m_hrtf_sample_locations.push_back(Vector2(0.0f,170.0f)); - m_hrtf_sample_locations.push_back(Vector2(0.0f,175.0f)); - m_hrtf_sample_locations.push_back(Vector2(0.0f,180.0f)); - m_hrtf_sample_locations.push_back(Vector2(10.0f,000.0f)); - m_hrtf_sample_locations.push_back(Vector2(10.0f,005.0f)); - m_hrtf_sample_locations.push_back(Vector2(10.0f,010.0f)); - m_hrtf_sample_locations.push_back(Vector2(10.0f,015.0f)); - m_hrtf_sample_locations.push_back(Vector2(10.0f,020.0f)); - m_hrtf_sample_locations.push_back(Vector2(10.0f,025.0f)); - m_hrtf_sample_locations.push_back(Vector2(10.0f,030.0f)); - m_hrtf_sample_locations.push_back(Vector2(10.0f,035.0f)); - m_hrtf_sample_locations.push_back(Vector2(10.0f,040.0f)); - m_hrtf_sample_locations.push_back(Vector2(10.0f,045.0f)); - m_hrtf_sample_locations.push_back(Vector2(10.0f,050.0f)); - m_hrtf_sample_locations.push_back(Vector2(10.0f,055.0f)); - m_hrtf_sample_locations.push_back(Vector2(10.0f,060.0f)); - m_hrtf_sample_locations.push_back(Vector2(10.0f,065.0f)); - m_hrtf_sample_locations.push_back(Vector2(10.0f,070.0f)); - m_hrtf_sample_locations.push_back(Vector2(10.0f,075.0f)); - m_hrtf_sample_locations.push_back(Vector2(10.0f,080.0f)); - m_hrtf_sample_locations.push_back(Vector2(10.0f,085.0f)); - m_hrtf_sample_locations.push_back(Vector2(10.0f,090.0f)); - m_hrtf_sample_locations.push_back(Vector2(10.0f,095.0f)); - m_hrtf_sample_locations.push_back(Vector2(10.0f,100.0f)); - m_hrtf_sample_locations.push_back(Vector2(10.0f,105.0f)); - m_hrtf_sample_locations.push_back(Vector2(10.0f,110.0f)); - m_hrtf_sample_locations.push_back(Vector2(10.0f,115.0f)); - m_hrtf_sample_locations.push_back(Vector2(10.0f,120.0f)); - m_hrtf_sample_locations.push_back(Vector2(10.0f,125.0f)); - m_hrtf_sample_locations.push_back(Vector2(10.0f,130.0f)); - m_hrtf_sample_locations.push_back(Vector2(10.0f,135.0f)); - m_hrtf_sample_locations.push_back(Vector2(10.0f,140.0f)); - m_hrtf_sample_locations.push_back(Vector2(10.0f,145.0f)); - m_hrtf_sample_locations.push_back(Vector2(10.0f,150.0f)); - m_hrtf_sample_locations.push_back(Vector2(10.0f,155.0f)); - m_hrtf_sample_locations.push_back(Vector2(10.0f,160.0f)); - m_hrtf_sample_locations.push_back(Vector2(10.0f,165.0f)); - m_hrtf_sample_locations.push_back(Vector2(10.0f,170.0f)); - m_hrtf_sample_locations.push_back(Vector2(10.0f,175.0f)); - m_hrtf_sample_locations.push_back(Vector2(10.0f,180.0f)); - m_hrtf_sample_locations.push_back(Vector2(20.0f,000.0f)); - m_hrtf_sample_locations.push_back(Vector2(20.0f,005.0f)); - m_hrtf_sample_locations.push_back(Vector2(20.0f,010.0f)); - m_hrtf_sample_locations.push_back(Vector2(20.0f,015.0f)); - m_hrtf_sample_locations.push_back(Vector2(20.0f,020.0f)); - m_hrtf_sample_locations.push_back(Vector2(20.0f,025.0f)); - m_hrtf_sample_locations.push_back(Vector2(20.0f,030.0f)); - m_hrtf_sample_locations.push_back(Vector2(20.0f,035.0f)); - m_hrtf_sample_locations.push_back(Vector2(20.0f,040.0f)); - m_hrtf_sample_locations.push_back(Vector2(20.0f,045.0f)); - m_hrtf_sample_locations.push_back(Vector2(20.0f,050.0f)); - m_hrtf_sample_locations.push_back(Vector2(20.0f,055.0f)); - m_hrtf_sample_locations.push_back(Vector2(20.0f,060.0f)); - m_hrtf_sample_locations.push_back(Vector2(20.0f,065.0f)); - m_hrtf_sample_locations.push_back(Vector2(20.0f,070.0f)); - m_hrtf_sample_locations.push_back(Vector2(20.0f,075.0f)); - m_hrtf_sample_locations.push_back(Vector2(20.0f,080.0f)); - m_hrtf_sample_locations.push_back(Vector2(20.0f,085.0f)); - m_hrtf_sample_locations.push_back(Vector2(20.0f,090.0f)); - m_hrtf_sample_locations.push_back(Vector2(20.0f,095.0f)); - m_hrtf_sample_locations.push_back(Vector2(20.0f,100.0f)); - m_hrtf_sample_locations.push_back(Vector2(20.0f,105.0f)); - m_hrtf_sample_locations.push_back(Vector2(20.0f,110.0f)); - m_hrtf_sample_locations.push_back(Vector2(20.0f,115.0f)); - m_hrtf_sample_locations.push_back(Vector2(20.0f,120.0f)); - m_hrtf_sample_locations.push_back(Vector2(20.0f,125.0f)); - m_hrtf_sample_locations.push_back(Vector2(20.0f,130.0f)); - m_hrtf_sample_locations.push_back(Vector2(20.0f,135.0f)); - m_hrtf_sample_locations.push_back(Vector2(20.0f,140.0f)); - m_hrtf_sample_locations.push_back(Vector2(20.0f,145.0f)); - m_hrtf_sample_locations.push_back(Vector2(20.0f,150.0f)); - m_hrtf_sample_locations.push_back(Vector2(20.0f,155.0f)); - m_hrtf_sample_locations.push_back(Vector2(20.0f,160.0f)); - m_hrtf_sample_locations.push_back(Vector2(20.0f,165.0f)); - m_hrtf_sample_locations.push_back(Vector2(20.0f,170.0f)); - m_hrtf_sample_locations.push_back(Vector2(20.0f,175.0f)); - m_hrtf_sample_locations.push_back(Vector2(20.0f,180.0f)); - m_hrtf_sample_locations.push_back(Vector2(30.0f,000.0f)); - m_hrtf_sample_locations.push_back(Vector2(30.0f,006.0f)); - m_hrtf_sample_locations.push_back(Vector2(30.0f,012.0f)); - m_hrtf_sample_locations.push_back(Vector2(30.0f,018.0f)); - m_hrtf_sample_locations.push_back(Vector2(30.0f,024.0f)); - m_hrtf_sample_locations.push_back(Vector2(30.0f,030.0f)); - m_hrtf_sample_locations.push_back(Vector2(30.0f,036.0f)); - m_hrtf_sample_locations.push_back(Vector2(30.0f,042.0f)); - m_hrtf_sample_locations.push_back(Vector2(30.0f,048.0f)); - m_hrtf_sample_locations.push_back(Vector2(30.0f,054.0f)); - m_hrtf_sample_locations.push_back(Vector2(30.0f,060.0f)); - m_hrtf_sample_locations.push_back(Vector2(30.0f,066.0f)); - m_hrtf_sample_locations.push_back(Vector2(30.0f,072.0f)); - m_hrtf_sample_locations.push_back(Vector2(30.0f,078.0f)); - m_hrtf_sample_locations.push_back(Vector2(30.0f,084.0f)); - m_hrtf_sample_locations.push_back(Vector2(30.0f,090.0f)); - m_hrtf_sample_locations.push_back(Vector2(30.0f,096.0f)); - m_hrtf_sample_locations.push_back(Vector2(30.0f,102.0f)); - m_hrtf_sample_locations.push_back(Vector2(30.0f,108.0f)); - m_hrtf_sample_locations.push_back(Vector2(30.0f,114.0f)); - m_hrtf_sample_locations.push_back(Vector2(30.0f,120.0f)); - m_hrtf_sample_locations.push_back(Vector2(30.0f,126.0f)); - m_hrtf_sample_locations.push_back(Vector2(30.0f,132.0f)); - m_hrtf_sample_locations.push_back(Vector2(30.0f,138.0f)); - m_hrtf_sample_locations.push_back(Vector2(30.0f,144.0f)); - m_hrtf_sample_locations.push_back(Vector2(30.0f,150.0f)); - m_hrtf_sample_locations.push_back(Vector2(30.0f,156.0f)); - m_hrtf_sample_locations.push_back(Vector2(30.0f,162.0f)); - m_hrtf_sample_locations.push_back(Vector2(30.0f,168.0f)); - m_hrtf_sample_locations.push_back(Vector2(30.0f,174.0f)); - m_hrtf_sample_locations.push_back(Vector2(30.0f,180.0f)); - m_hrtf_sample_locations.push_back(Vector2(40.0f,000.0f)); - m_hrtf_sample_locations.push_back(Vector2(40.0f,006.0f)); - m_hrtf_sample_locations.push_back(Vector2(40.0f,013.0f)); - m_hrtf_sample_locations.push_back(Vector2(40.0f,019.0f)); - m_hrtf_sample_locations.push_back(Vector2(40.0f,026.0f)); - m_hrtf_sample_locations.push_back(Vector2(40.0f,032.0f)); - m_hrtf_sample_locations.push_back(Vector2(40.0f,039.0f)); - m_hrtf_sample_locations.push_back(Vector2(40.0f,045.0f)); - m_hrtf_sample_locations.push_back(Vector2(40.0f,051.0f)); - m_hrtf_sample_locations.push_back(Vector2(40.0f,058.0f)); - m_hrtf_sample_locations.push_back(Vector2(40.0f,064.0f)); - m_hrtf_sample_locations.push_back(Vector2(40.0f,071.0f)); - m_hrtf_sample_locations.push_back(Vector2(40.0f,077.0f)); - m_hrtf_sample_locations.push_back(Vector2(40.0f,084.0f)); - m_hrtf_sample_locations.push_back(Vector2(40.0f,090.0f)); - m_hrtf_sample_locations.push_back(Vector2(40.0f,096.0f)); - m_hrtf_sample_locations.push_back(Vector2(40.0f,103.0f)); - m_hrtf_sample_locations.push_back(Vector2(40.0f,109.0f)); - m_hrtf_sample_locations.push_back(Vector2(40.0f,116.0f)); - m_hrtf_sample_locations.push_back(Vector2(40.0f,122.0f)); - m_hrtf_sample_locations.push_back(Vector2(40.0f,129.0f)); - m_hrtf_sample_locations.push_back(Vector2(40.0f,135.0f)); - m_hrtf_sample_locations.push_back(Vector2(40.0f,141.0f)); - m_hrtf_sample_locations.push_back(Vector2(40.0f,148.0f)); - m_hrtf_sample_locations.push_back(Vector2(40.0f,154.0f)); - m_hrtf_sample_locations.push_back(Vector2(40.0f,161.0f)); - m_hrtf_sample_locations.push_back(Vector2(40.0f,167.0f)); - m_hrtf_sample_locations.push_back(Vector2(40.0f,174.0f)); - m_hrtf_sample_locations.push_back(Vector2(40.0f,180.0f)); - m_hrtf_sample_locations.push_back(Vector2(50.0f,000.0f)); - m_hrtf_sample_locations.push_back(Vector2(50.0f,008.0f)); - m_hrtf_sample_locations.push_back(Vector2(50.0f,016.0f)); - m_hrtf_sample_locations.push_back(Vector2(50.0f,024.0f)); - m_hrtf_sample_locations.push_back(Vector2(50.0f,032.0f)); - m_hrtf_sample_locations.push_back(Vector2(50.0f,040.0f)); - m_hrtf_sample_locations.push_back(Vector2(50.0f,048.0f)); - m_hrtf_sample_locations.push_back(Vector2(50.0f,056.0f)); - m_hrtf_sample_locations.push_back(Vector2(50.0f,064.0f)); - m_hrtf_sample_locations.push_back(Vector2(50.0f,072.0f)); - m_hrtf_sample_locations.push_back(Vector2(50.0f,080.0f)); - m_hrtf_sample_locations.push_back(Vector2(50.0f,088.0f)); - m_hrtf_sample_locations.push_back(Vector2(50.0f,096.0f)); - m_hrtf_sample_locations.push_back(Vector2(50.0f,104.0f)); - m_hrtf_sample_locations.push_back(Vector2(50.0f,112.0f)); - m_hrtf_sample_locations.push_back(Vector2(50.0f,120.0f)); - m_hrtf_sample_locations.push_back(Vector2(50.0f,128.0f)); - m_hrtf_sample_locations.push_back(Vector2(50.0f,136.0f)); - m_hrtf_sample_locations.push_back(Vector2(50.0f,144.0f)); - m_hrtf_sample_locations.push_back(Vector2(50.0f,152.0f)); - m_hrtf_sample_locations.push_back(Vector2(50.0f,160.0f)); - m_hrtf_sample_locations.push_back(Vector2(50.0f,168.0f)); - m_hrtf_sample_locations.push_back(Vector2(50.0f,176.0f)); - m_hrtf_sample_locations.push_back(Vector2(60.0f,000.0f)); - m_hrtf_sample_locations.push_back(Vector2(60.0f,010.0f)); - m_hrtf_sample_locations.push_back(Vector2(60.0f,020.0f)); - m_hrtf_sample_locations.push_back(Vector2(60.0f,030.0f)); - m_hrtf_sample_locations.push_back(Vector2(60.0f,040.0f)); - m_hrtf_sample_locations.push_back(Vector2(60.0f,050.0f)); - m_hrtf_sample_locations.push_back(Vector2(60.0f,060.0f)); - m_hrtf_sample_locations.push_back(Vector2(60.0f,070.0f)); - m_hrtf_sample_locations.push_back(Vector2(60.0f,080.0f)); - m_hrtf_sample_locations.push_back(Vector2(60.0f,090.0f)); - m_hrtf_sample_locations.push_back(Vector2(60.0f,100.0f)); - m_hrtf_sample_locations.push_back(Vector2(60.0f,110.0f)); - m_hrtf_sample_locations.push_back(Vector2(60.0f,120.0f)); - m_hrtf_sample_locations.push_back(Vector2(60.0f,130.0f)); - m_hrtf_sample_locations.push_back(Vector2(60.0f,140.0f)); - m_hrtf_sample_locations.push_back(Vector2(60.0f,150.0f)); - m_hrtf_sample_locations.push_back(Vector2(60.0f,160.0f)); - m_hrtf_sample_locations.push_back(Vector2(60.0f,170.0f)); - m_hrtf_sample_locations.push_back(Vector2(60.0f,180.0f)); - m_hrtf_sample_locations.push_back(Vector2(70.0f,000.0f)); - m_hrtf_sample_locations.push_back(Vector2(70.0f,015.0f)); - m_hrtf_sample_locations.push_back(Vector2(70.0f,030.0f)); - m_hrtf_sample_locations.push_back(Vector2(70.0f,045.0f)); - m_hrtf_sample_locations.push_back(Vector2(70.0f,060.0f)); - m_hrtf_sample_locations.push_back(Vector2(70.0f,075.0f)); - m_hrtf_sample_locations.push_back(Vector2(70.0f,090.0f)); - m_hrtf_sample_locations.push_back(Vector2(70.0f,105.0f)); - m_hrtf_sample_locations.push_back(Vector2(70.0f,120.0f)); - m_hrtf_sample_locations.push_back(Vector2(70.0f,135.0f)); - m_hrtf_sample_locations.push_back(Vector2(70.0f,150.0f)); - m_hrtf_sample_locations.push_back(Vector2(70.0f,165.0f)); - m_hrtf_sample_locations.push_back(Vector2(70.0f,180.0f)); - m_hrtf_sample_locations.push_back(Vector2(80.0f,000.0f)); - m_hrtf_sample_locations.push_back(Vector2(80.0f,030.0f)); - m_hrtf_sample_locations.push_back(Vector2(80.0f,060.0f)); - m_hrtf_sample_locations.push_back(Vector2(80.0f,090.0f)); - m_hrtf_sample_locations.push_back(Vector2(80.0f,120.0f)); - m_hrtf_sample_locations.push_back(Vector2(80.0f,150.0f)); - m_hrtf_sample_locations.push_back(Vector2(80.0f,180.0f)); - m_hrtf_sample_locations.push_back(Vector2(90.0f,000.0f)); - - if(m_hrtf_data) { - delete m_hrtf_data; - } - m_hrtf_data = (float *)malloc(m_hrtf_sample_locations.size() * sizeof(float) * 128 * 4 * 2); - - for(int channel=0; channel < 2; channel++) { - m_hrtf_spectral[channel].clear(); - } - - int sample_index=0; - for(std::vector::iterator itr=m_hrtf_sample_locations.begin(); itr != m_hrtf_sample_locations.end(); itr++) { - Vector2 pos = *itr; - KRAudioSample *sample = getHRTFSample(pos); - for(int channel=0; channel < 2; channel++) { - KRDSP::SplitComplex spectral; - spectral.realp = m_hrtf_data + sample_index * 1024 + channel * 512; - spectral.imagp = m_hrtf_data + sample_index * 1024 + channel * 512 + 256; - sample->sample(0, 128, channel, spectral.realp, 1.0f, false); - memset(spectral.realp + 128, 0, sizeof(float) * 128); - memset(spectral.imagp, 0, sizeof(float) * 256); - KRDSP::FFTForward(m_fft_setup[8 - KRENGINE_AUDIO_BLOCK_LOG2N], &spectral, 8); - m_hrtf_spectral[channel][pos] = spectral; - } - sample_index++; - } - -} - -KRAudioSample *KRAudioManager::getHRTFSample(const Vector2 &hrtf_dir) -{ - //hrtf_kemar_H-10e000a.wav - char szName[64]; - sprintf(szName, "hrtf_kemar_H%de%03da", int(hrtf_dir.x), abs(int(hrtf_dir.y))); - - return get(szName); -} - -KRDSP::SplitComplex KRAudioManager::getHRTFSpectral(const Vector2 &hrtf_dir, const int channel) -{ - Vector2 dir = hrtf_dir; - int sample_channel = channel; - if(dir.y < 0) { - dir.y = -dir.y; - sample_channel = (channel + 1) % 2; - } - return m_hrtf_spectral[sample_channel][dir]; -} - -Vector2 KRAudioManager::getNearestHRTFSample(const Vector2 &dir) -{ - float elev_gran = 10.0f; - - - Vector2 dir_deg = dir * (180.0f / M_PI); - float elevation = floor(dir_deg.x / elev_gran + 0.5f) * elev_gran; - if(elevation < -40.0f) { - elevation = -40.0f; - } - - Vector2 min_direction; - bool first = true; - float min_distance = 360.0f; - for(std::vector::iterator itr = m_hrtf_sample_locations.begin(); itr != m_hrtf_sample_locations.end(); itr++) { - if(first) { - first = false; - min_direction = (*itr); - } else if((*itr).x == elevation) { - float distance = fabs(dir_deg.y - (*itr).y); - if(min_distance > distance) { - min_direction = (*itr); - min_distance = distance; - } - distance = fabs(dir_deg.y - -(*itr).y); - if(min_distance > distance) { - min_direction = (*itr); - min_direction.y = -min_direction.y; - min_distance = distance; - } - } - } - return min_direction; -} - -void KRAudioManager::getHRTFMix(const Vector2 &dir, Vector2 &dir1, Vector2 &dir2, Vector2 &dir3, Vector2 &dir4, float &mix1, float &mix2, float &mix3, float &mix4) -{ - float elev_gran = 10.0f; - - float elevation = dir.x * 180.0f / M_PI; - float azimuth = dir.y * 180.0f / M_PI; - - float elev1 = floor(elevation / elev_gran) * elev_gran; - float elev2 = ceil(elevation / elev_gran) * elev_gran; - float elev_blend = (elevation - elev1) / elev_gran; - if(elev1 < -40.0f) { - elev1 = -40.0f; - elev2 = -40.0f; - elev_blend = 0.0f; - } - - dir1.x = elev1; - dir2.x = elev1; - dir3.x = elev2; - dir4.x = elev2; - - bool first1 = true; - bool first2 = true; - bool first3 = true; - bool first4 = true; - for(std::vector::iterator itr = m_hrtf_sample_locations.begin(); itr != m_hrtf_sample_locations.end(); itr++) { - Vector2 sample_pos = *itr; - - if(sample_pos.x == elev1) { - if(sample_pos.y <= azimuth) { - if(first1) { - first1 = false; - dir1.y = sample_pos.y; - } else if(sample_pos.y > dir1.y) { - dir1.y = sample_pos.y; - } - } - if(-sample_pos.y <= azimuth) { - if(first1) { - first1 = false; - dir1.y = -sample_pos.y; - } else if(-sample_pos.y > dir1.y) { - dir1.y = -sample_pos.y; - } - } - - if(sample_pos.y > azimuth) { - if(first2) { - first2 = false; - dir2.y = sample_pos.y; - } else if(sample_pos.y < dir2.y) { - dir2.y = sample_pos.y; - } - } - if(-sample_pos.y > azimuth) { - if(first2) { - first2 = false; - dir2.y = -sample_pos.y; - } else if(-sample_pos.y < dir2.y) { - dir2.y = -sample_pos.y; - } - } - } - if(sample_pos.x == elev2) { - if(sample_pos.y <= azimuth) { - if(first3) { - first3 = false; - dir3.y = sample_pos.y; - } else if(sample_pos.y > dir3.y) { - dir3.y = sample_pos.y; - } - } - if(-sample_pos.y <= azimuth) { - if(first3) { - first3 = false; - dir3.y = -sample_pos.y; - } else if(-sample_pos.y > dir3.y) { - dir3.y = -sample_pos.y; - } - } - - if(sample_pos.y > azimuth) { - if(first4) { - first4 = false; - dir4.y = sample_pos.y; - } else if(sample_pos.y < dir4.y) { - dir4.y = sample_pos.y; - } - } - if(-sample_pos.y > azimuth) { - if(first4) { - first4 = false; - dir4.y = -sample_pos.y; - } else if(-sample_pos.y < dir4.y) { - dir4.y = -sample_pos.y; - } - } - } - } - - - float azim_blend1 = 0.0f; - if(dir2.y > dir1.y) { - azim_blend1 = (azimuth - dir1.y) / (dir2.y - dir1.y); - } - - float azim_blend2 = 0.0f; - if(dir4.y > dir3.y) { - azim_blend2 = (azimuth - dir3.y) / (dir4.y - dir3.y); - } - - mix1 = (1.0f - azim_blend1) * (1.0f - elev_blend); - mix2 = azim_blend1 * (1.0f - elev_blend); - mix3 = (1.0f - azim_blend2) * elev_blend; - mix4 = azim_blend2 * elev_blend; -} - -void KRAudioManager::initAudio() -{ - if(!m_initialized) { - m_initialized = true; - m_output_sample = KRENGINE_AUDIO_BLOCK_LENGTH; - - // initialize double-buffer for reverb input - int buffer_size = sizeof(float) * KRENGINE_REVERB_MAX_SAMPLES; // Reverb input is a single channel, circular buffered - m_reverb_input_samples = (float *)malloc(buffer_size); - memset(m_reverb_input_samples, 0, buffer_size); - m_reverb_input_next_sample = 0; - - m_output_accumulation = (float *)malloc(buffer_size * 2); // 2 channels - memset(m_output_accumulation, 0, buffer_size * 2); - m_output_accumulation_block_start = 0; - - m_workspace_data = (float *)malloc(KRENGINE_REVERB_WORKSPACE_SIZE * 6 * sizeof(float)); - m_workspace[0].realp = m_workspace_data + KRENGINE_REVERB_WORKSPACE_SIZE * 0; - m_workspace[0].imagp = m_workspace_data + KRENGINE_REVERB_WORKSPACE_SIZE * 1; - m_workspace[1].realp = m_workspace_data + KRENGINE_REVERB_WORKSPACE_SIZE * 2; - m_workspace[1].imagp = m_workspace_data + KRENGINE_REVERB_WORKSPACE_SIZE * 3; - m_workspace[2].realp = m_workspace_data + KRENGINE_REVERB_WORKSPACE_SIZE * 4; - m_workspace[2].imagp = m_workspace_data + KRENGINE_REVERB_WORKSPACE_SIZE * 5; - - m_reverb_sequence = 0; - for(int i=KRENGINE_AUDIO_BLOCK_LOG2N; i <= KRENGINE_REVERB_MAX_FFT_LOG2; i++) { - m_fft_setup[i - KRENGINE_AUDIO_BLOCK_LOG2N].create(i); - // FINDME, TODO.. Apple's vDSP only needs one - // KRDSP::FFTWorkspace, initialized with the maximum size - } - - // ----====---- Initialize HRTF Engine ----====---- - initHRTF(); - -#ifdef __APPLE__ - // Apple Core Audio - // ----====---- Initialize Core Audio Objects ----====---- - OSDEBUG(NewAUGraph(&m_auGraph)); - - // ---- Create output node ---- - AudioComponentDescription output_desc; - output_desc.componentType = kAudioUnitType_Output; -#if TARGET_OS_IPHONE - output_desc.componentSubType = kAudioUnitSubType_RemoteIO; -#else - output_desc.componentSubType = kAudioUnitSubType_DefaultOutput; -#endif - output_desc.componentFlags = 0; - output_desc.componentFlagsMask = 0; - output_desc.componentManufacturer = kAudioUnitManufacturer_Apple; - AUNode outputNode = 0; - OSDEBUG(AUGraphAddNode(m_auGraph, &output_desc, &outputNode)); - - // ---- Create mixer node ---- - AudioComponentDescription mixer_desc; - mixer_desc.componentType = kAudioUnitType_Mixer; - mixer_desc.componentSubType = kAudioUnitSubType_MultiChannelMixer; - mixer_desc.componentFlags = 0; - mixer_desc.componentFlagsMask = 0; - mixer_desc.componentManufacturer = kAudioUnitManufacturer_Apple; - AUNode mixerNode = 0; - OSDEBUG(AUGraphAddNode(m_auGraph, &mixer_desc, &mixerNode )); - - // ---- Connect mixer to output node ---- - OSDEBUG(AUGraphConnectNodeInput(m_auGraph, mixerNode, 0, outputNode, 0)); - - // ---- Open the audio graph ---- - OSDEBUG(AUGraphOpen(m_auGraph)); - - // ---- Get a handle to the mixer ---- - OSDEBUG(AUGraphNodeInfo(m_auGraph, mixerNode, NULL, &m_auMixer)); - - // ---- Add output channel to mixer ---- - UInt32 bus_count = 1; - OSDEBUG(AudioUnitSetProperty(m_auMixer, kAudioUnitProperty_ElementCount, kAudioUnitScope_Input, 0, &bus_count, sizeof(bus_count))); - - // ---- Attach render function to channel ---- - AURenderCallbackStruct renderCallbackStruct; - renderCallbackStruct.inputProc = &renderInput; - renderCallbackStruct.inputProcRefCon = this; - OSDEBUG(AUGraphSetNodeInputCallback(m_auGraph, mixerNode, 0, &renderCallbackStruct)); // 0 = mixer input number - - AudioStreamBasicDescription desc; - memset(&desc, 0, sizeof(desc)); - - UInt32 size = sizeof(desc); - memset(&desc, 0, sizeof(desc)); - OSDEBUG(AudioUnitGetProperty( m_auMixer, - kAudioUnitProperty_StreamFormat, - kAudioUnitScope_Input, - 0, // 0 = mixer input number - &desc, - &size)); - - KRSetAUCanonical(desc, 2, false); - desc.mSampleRate = 44100.0f; - - OSDEBUG(AudioUnitSetProperty(m_auMixer, - kAudioUnitProperty_StreamFormat, - kAudioUnitScope_Input, - 0, // 0 == mixer input number - &desc, - sizeof(desc))); - - // ---- Apply properties to mixer output ---- - OSDEBUG(AudioUnitSetProperty(m_auMixer, - kAudioUnitProperty_StreamFormat, - kAudioUnitScope_Output, - 0, // Always 0 for output bus - &desc, - sizeof(desc))); - - - memset(&desc, 0, sizeof(desc)); - size = sizeof(desc); - OSDEBUG(AudioUnitGetProperty(m_auMixer, - kAudioUnitProperty_StreamFormat, - kAudioUnitScope_Output, - 0, - &desc, - &size)); - - // ---- - KRSetAUCanonical(desc, 2, false); - desc.mSampleRate = 44100.0f; - - - // ---- - - OSDEBUG(AudioUnitSetProperty(m_auMixer, - kAudioUnitProperty_StreamFormat, - kAudioUnitScope_Output, - 0, - &desc, - sizeof(desc))); - - - OSDEBUG(AudioUnitSetParameter(m_auMixer, kMultiChannelMixerParam_Volume, kAudioUnitScope_Input, 0, 1.0, 0)); - OSDEBUG(AudioUnitSetParameter(m_auMixer, kMultiChannelMixerParam_Volume, kAudioUnitScope_Output, 0, 1.0, 0)); - - OSDEBUG(AUGraphInitialize(m_auGraph)); - - // ----====---- Start the audio system ----====---- - OSDEBUG(AUGraphStart(m_auGraph)); - -// CAShow(m_auGraph); -#endif // Core Audio - } -} - - -void KRAudioManager::cleanupAudio() -{ -#ifdef __APPLE__ - // Apple Core Audio - if(m_auGraph) { - OSDEBUG(AUGraphStop(m_auGraph)); - OSDEBUG(DisposeAUGraph(m_auGraph)); - m_auGraph = NULL; - m_auMixer = NULL; - } -#endif - - if(m_reverb_input_samples) { - free(m_reverb_input_samples); - m_reverb_input_samples = NULL; - } - - if(m_output_accumulation) { - free(m_output_accumulation); - m_output_accumulation = NULL; - } - - if(m_workspace_data) { - free(m_workspace_data); - m_workspace_data = NULL; - } - - if(m_hrtf_data) { - free(m_hrtf_data); - m_hrtf_data = NULL; - } - - for(int i=0; i < KRENGINE_MAX_REVERB_IMPULSE_MIX; i++) { - m_reverb_impulse_responses[i] = NULL; - m_reverb_impulse_responses_weight[i] = 0.0f; - } - - for(int i=KRENGINE_AUDIO_BLOCK_LOG2N; i <= KRENGINE_REVERB_MAX_FFT_LOG2; i++) { - m_fft_setup[i - KRENGINE_AUDIO_BLOCK_LOG2N].destroy(); - } -} - -KRAudioManager::~KRAudioManager() -{ - for(unordered_map::iterator name_itr=m_sounds.begin(); name_itr != m_sounds.end(); name_itr++) { - delete (*name_itr).second; - } - - cleanupAudio(); - - for(std::vector::iterator itr = m_bufferPoolIdle.begin(); itr != m_bufferPoolIdle.end(); itr++) { - delete *itr; - } -} - -void KRAudioManager::makeCurrentContext() -{ - initAudio(); -} - -void KRAudioManager::setListenerOrientationFromModelMatrix(const Matrix4 &modelMatrix) -{ - setListenerOrientation( - Matrix4::Dot(modelMatrix, Vector3(0.0, 0.0, 0.0)), - Vector3::Normalize(Matrix4::Dot(modelMatrix, Vector3(0.0, 0.0, -1.0)) - m_listener_position), - Vector3::Normalize(Matrix4::Dot(modelMatrix, Vector3(0.0, 1.0, 0.0)) - m_listener_position) - ); -} - -Vector3 &KRAudioManager::getListenerForward() -{ - return m_listener_forward; -} - -Vector3 &KRAudioManager::getListenerPosition() -{ - return m_listener_position; -} - -Vector3 &KRAudioManager::getListenerUp() -{ - return m_listener_up; -} - -void KRAudioManager::setListenerOrientation(const Vector3 &position, const Vector3 &forward, const Vector3 &up) -{ - m_listener_position = position; - m_listener_forward = forward; - m_listener_up = up; - - makeCurrentContext(); -} - -void KRAudioManager::add(KRAudioSample *sound) -{ - std::string lower_name = sound->getName(); - std::transform(lower_name.begin(), lower_name.end(), lower_name.begin(), ::tolower); - - unordered_map::iterator name_itr = m_sounds.find(lower_name); - if(name_itr != m_sounds.end()) { - delete (*name_itr).second; - (*name_itr).second = sound; - } else { - m_sounds[lower_name] = sound; - } -} - -KRAudioSample *KRAudioManager::load(const std::string &name, const std::string &extension, KRDataBlock *data) -{ - KRAudioSample *Sound = new KRAudioSample(getContext(), name, extension, data); - if(Sound) add(Sound); - return Sound; -} - -KRAudioSample *KRAudioManager::get(const std::string &name) -{ - std::string lower_name = name; - std::transform(lower_name.begin(), lower_name.end(), lower_name.begin(), ::tolower); - return m_sounds[lower_name]; -} - -KRDataBlock *KRAudioManager::getBufferData(int size) -{ - KRDataBlock *data; - // Note: We only store and recycle buffers with a size of CIRCA_AUDIO_MAX_BUFFER_SIZE - if(size == KRENGINE_AUDIO_MAX_BUFFER_SIZE && m_bufferPoolIdle.size() > 0) { - // Recycle a buffer from the pool - data = m_bufferPoolIdle.back(); - m_bufferPoolIdle.pop_back(); - } else { - data = new KRDataBlock(); - data->expand(size); - } - data->lock(); - return data; -} - -void KRAudioManager::recycleBufferData(KRDataBlock *data) -{ - if(data != NULL) { - data->unlock(); - if(data->getSize() == KRENGINE_AUDIO_MAX_BUFFER_SIZE && m_bufferPoolIdle.size() < KRENGINE_AUDIO_MAX_POOL_SIZE) { - m_bufferPoolIdle.push_back(data); - } else { - delete data; - } - } -} - -void KRAudioManager::activateAudioSource(KRAudioSource *audioSource) -{ - m_activeAudioSources.insert(audioSource); -} - -void KRAudioManager::deactivateAudioSource(KRAudioSource *audioSource) -{ - m_activeAudioSources.erase(audioSource); -} - -void KRAudioManager::_registerOpenAudioSample(KRAudioSample *audioSample) -{ - m_openAudioSamples.insert(audioSample); -} - -void KRAudioManager::_registerCloseAudioSample(KRAudioSample *audioSample) -{ - m_openAudioSamples.erase(audioSample); -} - -__int64_t KRAudioManager::getAudioFrame() -{ - return m_audio_frame; -} - -KRAudioBuffer *KRAudioManager::getBuffer(KRAudioSample &audio_sample, int buffer_index) -{ - // ----====---- Try to find the buffer in the cache ----====---- - for(std::vector::iterator itr=m_bufferCache.begin(); itr != m_bufferCache.end(); itr++) { - KRAudioBuffer *cache_buffer = *itr; - if(cache_buffer->getAudioSample() == &audio_sample && cache_buffer->getIndex() == buffer_index) { - return cache_buffer; - } - } - - // ----====---- Make room in the cache for a new buffer ----====---- - if(m_bufferCache.size() >= KRENGINE_AUDIO_MAX_POOL_SIZE) { - // delete a random entry from the cache -#ifdef __APPLE__ - int index_to_delete = arc4random() % m_bufferCache.size(); -#else - int index_to_delete = rand() % m_bufferCache.size(); -#endif - std::vector::iterator itr_to_delete = m_bufferCache.begin() + index_to_delete; - delete *itr_to_delete; - m_bufferCache.erase(itr_to_delete); - } - - // ----====---- Request new buffer, add to cache, and return it ----====---- - KRAudioBuffer *buffer = audio_sample.getBuffer(buffer_index); - m_bufferCache.push_back(buffer); - return buffer; -} - -float KRAudioManager::getGlobalReverbSendLevel() -{ - return m_global_reverb_send_level; -} - -void KRAudioManager::setGlobalReverbSendLevel(float send_level) -{ - m_global_reverb_send_level = send_level; -} - -float KRAudioManager::getGlobalGain() -{ - return m_global_gain; -} - -void KRAudioManager::setGlobalGain(float gain) -{ - m_global_gain = gain; -} - -float KRAudioManager::getGlobalAmbientGain() -{ - return m_global_ambient_gain; -} - -void KRAudioManager::setGlobalAmbientGain(float gain) -{ - m_global_ambient_gain = gain; -} - -void KRAudioManager::startFrame(float deltaTime) -{ - m_mutex.lock(); - - // ----====---- Determine Ambient Zone Contributions ----====---- - m_ambient_zone_weights.clear(); - m_ambient_zone_total_weight = 0.0f; // For normalizing zone weights - if(m_listener_scene) { - std::set ambient_zones = m_listener_scene->getAmbientZones(); - - for(std::set::iterator itr=ambient_zones.begin(); itr != ambient_zones.end(); itr++) { - KRAmbientZone *sphere = *itr; - siren_ambient_zone_weight_info zi; - - zi.weight = sphere->getContainment(m_listener_position); - if(zi.weight > 0.0f) { - if(m_ambient_zone_weights.find(sphere->getZone()) == m_ambient_zone_weights.end()) { - zi.ambient_zone = sphere; - zi.ambient_sample = get(sphere->getAmbient()); - m_ambient_zone_weights[sphere->getZone()] = zi; - m_ambient_zone_total_weight += zi.weight; - } else { - float prev_weight = m_ambient_zone_weights[sphere->getZone()].weight; - if(zi.weight > prev_weight) { - m_ambient_zone_total_weight -= prev_weight; - m_ambient_zone_weights[sphere->getZone()].weight = zi.weight; - m_ambient_zone_total_weight += zi.weight; - } - } - } - } - } - - // ----====---- Determine Reverb Zone Contributions ----====---- - m_reverb_zone_weights.clear(); - m_reverb_zone_total_weight = 0.0f; // For normalizing zone weights - if(m_listener_scene) { - std::set reverb_zones = m_listener_scene->getReverbZones(); - - for(std::set::iterator itr=reverb_zones.begin(); itr != reverb_zones.end(); itr++) { - KRReverbZone *sphere = *itr; - siren_reverb_zone_weight_info zi; - - zi.weight = sphere->getContainment(m_listener_position); - if(zi.weight > 0.0f) { - if(m_reverb_zone_weights.find(sphere->getZone()) == m_reverb_zone_weights.end()) { - zi.reverb_zone = sphere; - zi.reverb_sample = get(sphere->getReverb()); - m_reverb_zone_weights[sphere->getZone()] = zi; - m_reverb_zone_total_weight += zi.weight; - } else { - float prev_weight = m_reverb_zone_weights[sphere->getZone()].weight; - if(zi.weight > prev_weight) { - m_reverb_zone_total_weight -= prev_weight; - m_reverb_zone_weights[sphere->getZone()].weight = zi.weight; - m_reverb_zone_total_weight += zi.weight; - } - } - } - } - } - - - - // ----====---- Map Source Directions and Gains ----====---- - m_prev_mapped_sources.clear(); - m_mapped_sources.swap(m_prev_mapped_sources); - - Vector3 listener_right = Vector3::Cross(m_listener_forward, m_listener_up); - std::set active_sources = m_activeAudioSources; - - - for(std::set::iterator itr=active_sources.begin(); itr != active_sources.end(); itr++) { - KRAudioSource *source = *itr; - Vector3 source_world_position = source->getWorldTranslation(); - Vector3 diff = source_world_position - m_listener_position; - float distance = diff.magnitude(); - float gain = source->getGain() * m_global_gain / pow(KRMAX(distance / source->getReferenceDistance(), 1.0f), source->getRolloffFactor()); - - // apply minimum-cutoff so that we don't waste cycles processing very quiet / distant sound sources - gain = KRMAX(gain - KRENGINE_AUDIO_CUTOFF, 0.0f) / (1.0f - KRENGINE_AUDIO_CUTOFF); - - if(gain > 0.0f) { - - Vector3 source_listener_space = Vector3( - Vector3::Dot(listener_right, diff), - Vector3::Dot(m_listener_up, diff), - Vector3::Dot(m_listener_forward, diff) - ); - - - Vector3 source_dir = Vector3::Normalize(source_listener_space); - - - - if(source->getEnableOcclusion() && /* FINDME!! DISABLES CODE */ (false)) { - HitInfo hitinfo; - if(source->getScene().lineCast(m_listener_position, source_world_position, hitinfo, KRAKEN_COLLIDER_AUDIO)) { - gain = 0.0f; - } - } - - Vector2 source_dir2 = Vector2::Normalize(Vector2(source_dir.x, source_dir.z)); - float azimuth = -atan2(source_dir2.x, -source_dir2.y); - float elevation = atan( source_dir.y / sqrt(source_dir.x * source_dir.x + source_dir.z * source_dir.z)); - - Vector2 adjusted_source_dir = Vector2(elevation, azimuth); - - if(!m_high_quality_hrtf) { - adjusted_source_dir = getNearestHRTFSample(adjusted_source_dir); - } - - // Click Removal - Add ramping of gain changes for audio sources that are continuing to play - float gain_anticlick = 0.0f; - std::pair > >::iterator, unordered_multimap > >::iterator> prev_range = m_prev_mapped_sources.equal_range(adjusted_source_dir); - for(unordered_multimap > >::iterator prev_itr=prev_range.first; prev_itr != prev_range.second; prev_itr++) { - if( (*prev_itr).second.first == source) { - gain_anticlick = (*prev_itr).second.second.second; - break; - } - } - - m_mapped_sources.insert(std::pair > >(adjusted_source_dir, std::pair >(source, std::pair(gain_anticlick, gain)))); - } - } - - // Click Removal - Map audio sources for ramp-down of gain for audio sources that have been squelched by attenuation - for(unordered_multimap > >::iterator itr=m_prev_mapped_sources.begin(); itr != m_prev_mapped_sources.end(); itr++) { - - KRAudioSource *source = (*itr).second.first; - float source_prev_gain = (*itr).second.second.second; - if(source->isPlaying() && source_prev_gain > 0.0f) { - // Only create ramp-down channels for 3d sources that have been squelched by attenuation; this is not necessary if the sample has completed playing - Vector2 source_position = (*itr).first; - - std::pair > >::iterator, unordered_multimap > >::iterator> new_range = m_mapped_sources.equal_range(source_position); - bool already_merged = false; - for(unordered_multimap > >::iterator new_itr=new_range.first; new_itr != new_range.second; new_itr++) { - if( (*new_itr).second.first == source) { - already_merged = true; - break; - } - } - if(!already_merged) { - - // source gain becomes anticlick gain and gain becomes 0 for anti-click ramp-down. - m_mapped_sources.insert(std::pair > >(source_position, std::pair >(source, std::pair(source_prev_gain, 0.0f)))); - } - } - } - - m_anticlick_block = true; - m_mutex.unlock(); -} - -void KRAudioManager::renderAmbient() -{ - if(m_listener_scene) { - - int output_offset = (m_output_accumulation_block_start) % (KRENGINE_REVERB_MAX_SAMPLES * KRENGINE_MAX_OUTPUT_CHANNELS); - float *buffer = m_workspace[0].realp; - - for(unordered_map::iterator zone_itr=m_ambient_zone_weights.begin(); zone_itr != m_ambient_zone_weights.end(); zone_itr++) { - siren_ambient_zone_weight_info zi = (*zone_itr).second; - float gain = (*zone_itr).second.weight * zi.ambient_zone->getAmbientGain() * m_global_ambient_gain * m_global_gain; - - KRAudioSample *source_sample = zi.ambient_sample; - if(source_sample) { - for(int channel=0; channel < KRENGINE_MAX_OUTPUT_CHANNELS; channel++) { - source_sample->sample(getContext().getAudioManager()->getAudioFrame(), KRENGINE_AUDIO_BLOCK_LENGTH, channel, buffer, gain, true); - KRDSP::Accumulate(m_output_accumulation + output_offset + channel, KRENGINE_MAX_OUTPUT_CHANNELS, - buffer, 1, - KRENGINE_AUDIO_BLOCK_LENGTH); - } - } - } - } -} - -void KRAudioManager::renderHRTF() -{ - KRDSP::SplitComplex *hrtf_accum = m_workspace + 0; - KRDSP::SplitComplex *hrtf_impulse = m_workspace + 1; - KRDSP::SplitComplex *hrtf_convolved = m_workspace + 1; // We only need hrtf_impulse or hrtf_convolved at once; we can recycle the buffer - KRDSP::SplitComplex *hrtf_sample = m_workspace + 2; - - int impulse_response_channels = 2; - int hrtf_frames = 128; - int fft_size = 256; - int fft_size_log2 = 8; - - for(int channel=0; channel > >::iterator itr=m_mapped_sources.begin(); - while(itr != m_mapped_sources.end()) { - // Batch together sound sources that are emitted from the same direction - Vector2 source_direction = (*itr).first; - KRAudioSource *source = (*itr).second.first; - float gain_anticlick = (*itr).second.second.first; - float gain = (*itr).second.second.second; - - - // If this is the first or only sample, write directly to the first half of the FFT input buffer - // Subsequent samples write to the second half of the FFT input buffer, which is then added to the first half (the second half will be zero'ed out anyways and works as a convenient temporary buffer) - float *sample_buffer = first_source ? hrtf_sample->realp : hrtf_sample->realp + KRENGINE_AUDIO_BLOCK_LENGTH; - - if(gain != gain_anticlick && m_anticlick_block) { - // Sample and perform anti-click filtering - source->sample(KRENGINE_AUDIO_BLOCK_LENGTH, 0, sample_buffer, 1.0); - float ramp_gain = gain_anticlick; - float ramp_step = (gain - gain_anticlick) / KRENGINE_AUDIO_ANTICLICK_SAMPLES; - KRDSP::ScaleRamp(sample_buffer, ramp_gain, ramp_step, KRENGINE_AUDIO_ANTICLICK_SAMPLES); - if(KRENGINE_AUDIO_BLOCK_LENGTH > KRENGINE_AUDIO_ANTICLICK_SAMPLES) { - KRDSP::Scale(sample_buffer + KRENGINE_AUDIO_ANTICLICK_SAMPLES, gain, KRENGINE_AUDIO_BLOCK_LENGTH - KRENGINE_AUDIO_ANTICLICK_SAMPLES); - } - } else { - // Don't need to perform anti-click filtering, so just sample - source->sample(KRENGINE_AUDIO_BLOCK_LENGTH, 0, sample_buffer, gain); - } - - if(first_source) { - first_source = false; - } else { - // Accumulate samples on subsequent sources - KRDSP::Accumulate(hrtf_sample->realp, 1, sample_buffer, 1, KRENGINE_AUDIO_BLOCK_LENGTH); - } - - itr++; - - bool end_of_group = false; - if(itr == m_mapped_sources.end()) { - end_of_group = true; - } else { - Vector2 next_direction = (*itr).first; - end_of_group = next_direction != source_direction; - } - - if(end_of_group) { - // ----====---- We have reached the end of a batch; convolve with HRTF impulse response and add to output accumulation buffer ----====---- - first_source = true; - - memset(hrtf_sample->realp + hrtf_frames, 0, sizeof(float) * hrtf_frames); - memset(hrtf_sample->imagp, 0, sizeof(float) * fft_size); - - KRDSP::SplitComplex hrtf_spectral; - - if(m_high_quality_hrtf) { - // High quality, interpolated HRTF - hrtf_spectral = *hrtf_accum; - - float mix[4]; - Vector2 dir[4]; - - getHRTFMix(source_direction, dir[0], dir[1], dir[2], dir[3], mix[0], mix[1], mix[2], mix[3]); - - - memset(hrtf_accum->realp, 0, sizeof(float) * fft_size); - memset(hrtf_accum->imagp, 0, sizeof(float) * fft_size); - - for(int i=0; i < 1 /*4 */; i++) { - if(mix[i] > 0.0f) { - KRDSP::SplitComplex hrtf_impulse_sample = getHRTFSpectral(dir[i], channel); - KRDSP::ScaleCopy(&hrtf_impulse_sample, mix[i], hrtf_impulse, fft_size); - KRDSP::Accumulate(hrtf_accum, hrtf_impulse, fft_size); - } - } - } else { - // Low quality, non-interpolated HRTF - hrtf_spectral = getHRTFSpectral(source_direction, channel); - } - - float scale = 0.5f / fft_size; - - KRDSP::FFTForward(m_fft_setup[fft_size_log2 - KRENGINE_AUDIO_BLOCK_LOG2N], hrtf_sample, fft_size_log2); - KRDSP::Multiply(hrtf_sample, &hrtf_spectral, hrtf_convolved, fft_size); - KRDSP::FFTInverse(m_fft_setup[fft_size_log2 - KRENGINE_AUDIO_BLOCK_LOG2N], hrtf_convolved, fft_size_log2); - KRDSP::Scale(hrtf_convolved->realp, scale, fft_size); - - int output_offset = (m_output_accumulation_block_start) % (KRENGINE_REVERB_MAX_SAMPLES * KRENGINE_MAX_OUTPUT_CHANNELS); - int frames_left = fft_size; - while(frames_left) { - int frames_to_process = (KRENGINE_REVERB_MAX_SAMPLES * KRENGINE_MAX_OUTPUT_CHANNELS - output_offset) / KRENGINE_MAX_OUTPUT_CHANNELS; - if(frames_to_process > frames_left) frames_to_process = frames_left; - KRDSP::Accumulate(m_output_accumulation + output_offset + channel, KRENGINE_MAX_OUTPUT_CHANNELS, - hrtf_convolved->realp + fft_size - frames_left, 1, - frames_to_process); - frames_left -= frames_to_process; - output_offset = (output_offset + frames_to_process * KRENGINE_MAX_OUTPUT_CHANNELS) % (KRENGINE_REVERB_MAX_SAMPLES * KRENGINE_MAX_OUTPUT_CHANNELS); - } - } - } - } -} - -void KRAudioManager::renderITD() -{ - // FINDME, TODO - Need Inter-Temperal based phase shifting to support 3-d spatialized audio without headphones - - /* - - - // hrtf_kemar_H-10e000a.wav - - float speed_of_sound = 1126.0f; // feed per second FINDME - TODO - This needs to be configurable for scenes with different units - float head_radius = 0.7431f; // 0.74ft = 22cm - float half_max_itd_time = head_radius / speed_of_sound / 2.0f; // half of ITD time (Interaural time difference) when audio source is directly 90 degrees azimuth to one ear. - - // Vector3 m_listener_position; - // Vector3 m_listener_forward; - // Vector3 m_listener_up; - - Vector3 listener_right = Vector3::Cross(m_listener_forward, m_listener_up); - Vector3 listener_right_ear = m_listener_position + listener_right * head_radius / 2.0f; - Vector3 listener_left_ear = m_listener_position - listener_right * head_radius / 2.0f; - - // Get a pointer to the dataBuffer of the AudioBufferList - - Float32 *outA = (Float32 *)ioData->mBuffers[0].mData; - Float32 *outB = (Float32 *)ioData->mBuffers[1].mData; // Non-Interleaved only - - - // ----====---- Zero out accumulation / output buffer ----====---- - for (UInt32 i = 0; i < inNumberFrames; ++i) { - - // Interleaved - // outA[i*2] = (Float32)left_channel; - // outA[i*2 + 1] = (Float32)right_channel; - - // Non-Interleaved - outA[i] = (Float32)0.0f; - outB[i] = (Float32)0.0f; - } - - // ----====---- Render direct / HRTF audio ----====---- - for(std::set::iterator itr=m_activeAudioSources.begin(); itr != m_activeAudioSources.end(); itr++) { - KRAudioSource *source = *itr; - Vector3 listener_to_source = source->getWorldTranslation() - m_listener_position; - Vector3 right_ear_to_source = source->getWorldTranslation() - listener_right_ear; - Vector3 left_ear_to_source = source->getWorldTranslation() - listener_left_ear; - Vector3 source_direction = Vector3::Normalize(listener_to_source); - float right_ear_distance = right_ear_to_source.magnitude(); - float left_ear_distance = left_ear_to_source.magnitude(); - float right_itd_time = right_ear_distance / speed_of_sound; - float left_itd_time = left_ear_distance / speed_of_sound; - - float rolloff_factor = source->getRolloffFactor(); - float left_gain = 1.0f / pow(left_ear_distance / rolloff_factor, 2.0f); - float right_gain = 1.0f / pow(left_ear_distance / rolloff_factor, 2.0f); - if(left_gain > 1.0f) left_gain = 1.0f; - if(right_gain > 1.0f) right_gain = 1.0f; - left_gain *= source->getGain(); - right_gain *= source->getGain(); - int left_itd_offset = (int)(left_itd_time * 44100.0f); - int right_itd_offset = (int)(right_itd_time * 44100.0f); - KRAudioSample *sample = source->getAudioSample(); - if(sample) { - __int64_t source_start_frame = source->getStartAudioFrame(); - int sample_frame = (int)(m_audio_frame - source_start_frame); - for (UInt32 i = 0; i < inNumberFrames; ++i) { - float left_channel=sample->sample(sample_frame + left_itd_offset, 44100, 0) * left_gain; - float right_channel = sample->sample(sample_frame + right_itd_offset, 44100, 0) * right_gain; - - // left_channel = 0.0f; - // right_channel = 0.0f; - - // Interleaved - // outA[i*2] = (Float32)left_channel; - // outA[i*2 + 1] = (Float32)right_channel; - - // Non-Interleaved - outA[i] += (Float32)left_channel; - outB[i] += (Float32)right_channel; - sample_frame++; - } - } - } - - KRAudioSample *reverb_impulse_response = getContext().getAudioManager()->get("test_reverb"); - - // ----====---- Render Indirect / Reverb channel ----====---- - int buffer_frame_start=0; - int remaining_frames = inNumberFrames - buffer_frame_start; - while(remaining_frames) { - int frames_processed = remaining_frames; - if(frames_processed > KRENGINE_REVERB_FILTER_LENGTH) { - frames_processed = KRENGINE_REVERB_FILTER_LENGTH; - } - bool first_source = true; - for(std::set::iterator itr=m_activeAudioSources.begin(); itr != m_activeAudioSources.end(); itr++) { - KRAudioSource *source = *itr; - KRAudioSample *sample = source->getAudioSample(); - if(sample) { - __int64_t source_start_frame = source->getStartAudioFrame(); - int sample_frame = (int)(m_audio_frame - source_start_frame + buffer_frame_start); - - - - - for (UInt32 i = 0; i < inNumberFrames; ++i) { - if(first_source) { - sample->sample(sample_frame, 44100, 0, m_reverb_input_next_sample); - } - sample->sampleAdditive(sample_frame, 44100, 0, m_reverb_input_next_sample); - if(m_reverb_input_next_sample >= KRENGINE_REVERB_MAX_SAMPLES) { - m_reverb_input_next_sample -= KRENGINE_REVERB_MAX_SAMPLES; - } - } - } - first_source = false; - } - - - if(reverb_impulse_response) { - // Perform FFT Convolution for Impulse-response based reverb - - memcpy(m_reverb_accumulation, m_reverb_accumulation + KRENGINE_REVERB_FILTER_LENGTH, KRENGINE_REVERB_FILTER_LENGTH * sizeof(float)); - memset(m_reverb_accumulation + KRENGINE_REVERB_FILTER_LENGTH, 0, KRENGINE_REVERB_FILTER_LENGTH * sizeof(float)); - } - - buffer_frame_start += frames_processed; - remaining_frames = inNumberFrames - buffer_frame_start; - } - - - */ -} - -static bool audioIsMuted = false; -static bool audioShouldBecomeMuted = false; -static bool audioShouldBecomeUnmuted = false; - -void audioLimit_Mute(bool onNotOff) { - if (onNotOff) { - if (audioIsMuted) { - audioShouldBecomeMuted = false; - audioShouldBecomeUnmuted = false; - return; - } - audioShouldBecomeMuted = true; - audioShouldBecomeUnmuted = false; - } - else { - if (!audioIsMuted) { - audioShouldBecomeMuted = false; - audioShouldBecomeUnmuted = false; - return; - } - audioShouldBecomeMuted = false; - audioShouldBecomeUnmuted = true; - } -} - -float audioGetLimitParameters_Stereo(float *buffer, unsigned long framesize, - unsigned long *attack_sample_position, float *peak) -{ - float limitvol = 1.0; - long attack_position = -1; - *peak = 0.0; - float max = 0.0; - float amplitude = 0.0; - - float *src = buffer; - for (unsigned long i = 0; i < framesize * 2; i++) { - amplitude = fabs(*src); src++; - if (amplitude > max) max = amplitude; - if (amplitude > 0.995) if (attack_position < 0) attack_position = (i+1) / 2; - } - if (max > 0.995) limitvol = 0.995 / max; - *peak = max; - - if (attack_position < 0) attack_position = framesize; - *attack_sample_position = (unsigned long) attack_position; - return limitvol; -} // returns the new limit volume, *attack_sample_position tells how fast we need to reach the new limit - -void audioLimit_Stereo(float *stereo_buffer, unsigned long framesize) -{ - static float limit_value = 1.0; - - // (1) get the limiting parameters for the incoming audio data - float previouslimitvol = limit_value; - float peak; - unsigned long attack_sample_position = framesize; - - // (1a) check for a mute or unmute state then get the next limit volume - float nextlimitvol = 0.0; - if (audioIsMuted && audioShouldBecomeUnmuted) { audioIsMuted = false; audioShouldBecomeUnmuted = false; } - if (audioShouldBecomeMuted) { audioIsMuted = true; audioShouldBecomeMuted = false; } - if (!audioIsMuted) nextlimitvol = audioGetLimitParameters_Stereo(stereo_buffer, framesize, &attack_sample_position, &peak); - - // (1b) if no limiting is needed then return - if ((1.0 == nextlimitvol) && (1.0 == previouslimitvol)) { return; } // no limiting necessary - - // (2) calculate limiting factors - float deltavol = 0.0; - if (previouslimitvol != nextlimitvol) { - deltavol = (nextlimitvol - previouslimitvol) / (float) attack_sample_position; - } - - // (3) do the limiting - float *src = stereo_buffer; - - if (0.0 == deltavol) { // fixed volume - for (unsigned long i=0; i < framesize; i++) { - *src = *src * nextlimitvol; - src++; - *src = *src * nextlimitvol; - src++; - } - } - else { - for (unsigned long i=0; i < attack_sample_position; i++) { // attack phase - *src = *src * previouslimitvol; - src++; - *src = *src * previouslimitvol; - src++; - previouslimitvol += deltavol; - } - if (nextlimitvol < 1.0) { // plateau phase - for (unsigned long i = attack_sample_position; i < framesize; i++) { - *src = *src * nextlimitvol; - src++; - *src = *src * nextlimitvol; - src++; - } - } - } - - // (4) save our limit level for next time - limit_value = nextlimitvol; -} - -void KRAudioManager::mute(bool onNotOff) -{ - audioLimit_Mute(onNotOff); -} - -void KRAudioManager::renderLimiter() -{ - int output_offset = (m_output_accumulation_block_start) % (KRENGINE_REVERB_MAX_SAMPLES * KRENGINE_MAX_OUTPUT_CHANNELS); - float *output = m_output_accumulation + output_offset; - unsigned long numframes = KRENGINE_AUDIO_BLOCK_LENGTH; - audioLimit_Stereo(output, numframes); -} - -void KRAudioManager::goToSleep() -{ - cleanupAudio(); -} +// +// FileManager.cpp +// KREngine +// +// Copyright 2012 Kearwood Gilbert. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other materials +// provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// The views and conclusions contained in the software and documentation are those of the +// authors and should not be interpreted as representing official policies, either expressed +// or implied, of Kearwood Gilbert. +// + +#include "KRAudioManager.h" +#include "KRAudioSample.h" +#include "KREngine-common.h" +#include "KRDataBlock.h" +#include "KRAudioBuffer.h" +#include "KRContext.h" +#include "KRCollider.h" +#include "KRDSP.h" + +KRAudioManager::KRAudioManager(KRContext &context) + : KRContextObject(context) + , m_initialized(false) +{ + m_enable_audio = true; + m_enable_hrtf = true; + m_enable_reverb = true; + m_reverb_max_length = 8.0f; + + m_anticlick_block = true; +#ifdef __APPLE__ + mach_timebase_info(&m_timebase_info); +#endif + + m_high_quality_hrtf = false; + + m_listener_scene = NULL; + + m_global_gain = 0.20f; + m_global_reverb_send_level = 1.0f; + m_global_ambient_gain = 1.0f; + +#ifdef __APPLE__ + // Apple Core Audio + m_auGraph = NULL; + m_auMixer = NULL; +#endif + + m_audio_frame = 0; + + m_output_sample = 0; + + + m_reverb_input_samples = NULL; + m_reverb_input_next_sample = 0; + + m_workspace_data = NULL; + m_reverb_sequence = 0; + + m_hrtf_data = NULL; + + for(int i=0; i < KRENGINE_MAX_REVERB_IMPULSE_MIX; i++) { + m_reverb_impulse_responses[i] = NULL; + m_reverb_impulse_responses_weight[i] = 0.0f; + } + m_output_accumulation = NULL; +} + +unordered_map &KRAudioManager::getSounds() +{ + return m_sounds; +} + +bool KRAudioManager::getEnableAudio() +{ + return m_enable_audio; +} +void KRAudioManager::setEnableAudio(bool enable) +{ + m_enable_audio = enable; +} + +bool KRAudioManager::getEnableHRTF() +{ + return m_enable_hrtf; +} +void KRAudioManager::setEnableHRTF(bool enable) +{ + m_enable_hrtf = enable; +} + +bool KRAudioManager::getEnableReverb() +{ + return m_enable_reverb; +} + +float KRAudioManager::getReverbMaxLength() +{ + return m_reverb_max_length; +} + +void KRAudioManager::setEnableReverb(bool enable) +{ + m_enable_reverb = enable; +} + +void KRAudioManager::setReverbMaxLength(float max_length) +{ + m_reverb_max_length = max_length; +} + +KRScene *KRAudioManager::getListenerScene() +{ + return m_listener_scene; +} + +void KRAudioManager::setListenerScene(KRScene *scene) +{ + m_listener_scene = scene; +} + +#ifdef __APPLE__ +// Apple Core Audio +void KRAudioManager::renderAudio(UInt32 inNumberFrames, AudioBufferList *ioData) +{ + // uint64_t start_time = mach_absolute_time(); + Float32 *outA = (Float32 *)ioData->mBuffers[0].mData; + Float32 *outB = (Float32 *)ioData->mBuffers[1].mData; // Non-Interleaved only + + int output_frame = 0; + + while(output_frame < inNumberFrames) { + int frames_ready = KRENGINE_AUDIO_BLOCK_LENGTH - m_output_sample; + if(frames_ready == 0) { + renderBlock(); + m_output_sample = 0; + frames_ready = KRENGINE_AUDIO_BLOCK_LENGTH; + } + + int frames_processed = inNumberFrames - output_frame; + if(frames_processed > frames_ready) frames_processed = frames_ready; + + float *block_data = getBlockAddress(0); + + for(int i=0; i < frames_processed; i++) { + + float left_channel = block_data[m_output_sample * KRENGINE_MAX_OUTPUT_CHANNELS]; + float right_channel = block_data[m_output_sample * KRENGINE_MAX_OUTPUT_CHANNELS + 1]; + m_output_sample++; + + // Interleaved + // outA[i*2] = (Float32)left_channel; + // outA[i*2 + 1] = (Float32)right_channel; + + // Non-Interleaved + outA[output_frame] = (Float32)left_channel; + outB[output_frame] = (Float32)right_channel; + output_frame++; + } + } + +// uint64_t end_time = mach_absolute_time(); +// uint64_t duration = (end_time - start_time) * m_timebase_info.numer / m_timebase_info.denom; // Nanoseconds +// double ms = duration; +// ms = ms / 1000000.0; +// uint64_t max_duration = (uint64_t)inNumberFrames * 1000000000 / 44100; +// fprintf(stderr, "audio load: %5.1f%% hrtf channels: %li\n", (float)(duration * 1000 / max_duration) / 10.0f, m_mapped_sources.size()); +// printf("ms %2.3f frames %ld audio load: %5.1f%% hrtf channels: %li\n", ms, (unsigned long) inNumberFrames, (float)(duration * 1000 / max_duration) / 10.0f, m_mapped_sources.size()); +} +#endif + +float *KRAudioManager::getBlockAddress(int block_offset) +{ + return m_output_accumulation + (m_output_accumulation_block_start + block_offset * KRENGINE_AUDIO_BLOCK_LENGTH * KRENGINE_MAX_OUTPUT_CHANNELS) % (KRENGINE_REVERB_MAX_SAMPLES * KRENGINE_MAX_OUTPUT_CHANNELS); +} + +void KRAudioManager::renderReverbImpulseResponse(int impulse_response_offset, int frame_count_log2) +{ + int frame_count = 1 << frame_count_log2; + int fft_size = frame_count * 2; + int fft_size_log2 = frame_count_log2 + 1; + + KRDSP::SplitComplex reverb_sample_data_complex = m_workspace[0]; + KRDSP::SplitComplex impulse_block_data_complex = m_workspace[1]; + KRDSP::SplitComplex conv_data_complex = m_workspace[2]; + + int reverb_offset = (m_reverb_input_next_sample + KRENGINE_AUDIO_BLOCK_LENGTH - frame_count); + if(reverb_offset < 0) { + reverb_offset += KRENGINE_REVERB_MAX_SAMPLES; + } else { + reverb_offset = reverb_offset % KRENGINE_REVERB_MAX_SAMPLES; + } + + int frames_left = frame_count; + while(frames_left) { + int frames_to_process = KRENGINE_REVERB_MAX_SAMPLES - reverb_offset; + if(frames_to_process > frames_left) frames_to_process = frames_left; + memcpy(reverb_sample_data_complex.realp + frame_count - frames_left, m_reverb_input_samples + reverb_offset, frames_to_process * sizeof(float)); + + frames_left -= frames_to_process; + reverb_offset = (reverb_offset + frames_to_process) % KRENGINE_REVERB_MAX_SAMPLES; + } + + memset(reverb_sample_data_complex.realp + frame_count, 0, frame_count * sizeof(float)); + memset(reverb_sample_data_complex.imagp, 0, fft_size * sizeof(float)); + + KRDSP::FFTForward(m_fft_setup[fft_size_log2 - KRENGINE_AUDIO_BLOCK_LOG2N], &reverb_sample_data_complex, fft_size_log2); + + float scale = 0.5f / fft_size; + + int impulse_response_channels = 2; + for(int channel=0; channel < impulse_response_channels; channel++) { + bool first_sample = true; + for(unordered_map::iterator zone_itr=m_reverb_zone_weights.begin(); zone_itr != m_reverb_zone_weights.end(); zone_itr++) { + siren_reverb_zone_weight_info zi = (*zone_itr).second; + if(zi.reverb_sample) { + if(impulse_response_offset < KRMIN(zi.reverb_sample->getFrameCount(), m_reverb_max_length * 44100)) { // Optimization - when mixing multiple impulse responses (i.e. fading between reverb zones), do not process blocks past the end of a shorter impulse response sample when they differ in length + if(first_sample) { + // If this is the first or only sample, write directly to the first half of the FFT input buffer + first_sample = false; + zi.reverb_sample->sample(impulse_response_offset, frame_count, channel, impulse_block_data_complex.realp, zi.weight, false); + } else { + // Subsequent samples write to the second half of the FFT input buffer, which is then added to the first half (the second half will be zero'ed out anyways and works as a convenient temporary buffer) + zi.reverb_sample->sample(impulse_response_offset, frame_count, channel, impulse_block_data_complex.realp + frame_count, zi.weight, false); + KRDSP::Accumulate(impulse_block_data_complex.realp, 1, impulse_block_data_complex.realp + frame_count, 1, frame_count); + } + } + + } + } + memset(impulse_block_data_complex.realp + frame_count, 0, frame_count * sizeof(float)); + memset(impulse_block_data_complex.imagp, 0, fft_size * sizeof(float)); + + + + KRDSP::FFTForward(m_fft_setup[fft_size_log2 - KRENGINE_AUDIO_BLOCK_LOG2N], &impulse_block_data_complex, fft_size_log2); + KRDSP::Multiply(&reverb_sample_data_complex, &impulse_block_data_complex, &conv_data_complex, fft_size); + KRDSP::FFTInverse(m_fft_setup[fft_size_log2 - KRENGINE_AUDIO_BLOCK_LOG2N], &conv_data_complex, fft_size_log2); + KRDSP::Scale(conv_data_complex.realp, scale, fft_size); + + + int output_offset = (m_output_accumulation_block_start + impulse_response_offset * KRENGINE_MAX_OUTPUT_CHANNELS) % (KRENGINE_REVERB_MAX_SAMPLES * KRENGINE_MAX_OUTPUT_CHANNELS); + frames_left = fft_size; + while(frames_left) { + int frames_to_process = (KRENGINE_REVERB_MAX_SAMPLES * KRENGINE_MAX_OUTPUT_CHANNELS - output_offset) / KRENGINE_MAX_OUTPUT_CHANNELS; + if(frames_to_process > frames_left) frames_to_process = frames_left; + KRDSP::Accumulate(m_output_accumulation + output_offset + channel, KRENGINE_MAX_OUTPUT_CHANNELS, + conv_data_complex.realp + fft_size - frames_left, 1, + frames_to_process); + frames_left -= frames_to_process; + output_offset = (output_offset + frames_to_process * KRENGINE_MAX_OUTPUT_CHANNELS) % (KRENGINE_REVERB_MAX_SAMPLES * KRENGINE_MAX_OUTPUT_CHANNELS); + } + } +} + +void KRAudioManager::renderReverb() +{ + float reverb_data[KRENGINE_AUDIO_BLOCK_LENGTH]; + + float *reverb_accum = m_reverb_input_samples + m_reverb_input_next_sample; + memset(reverb_accum, 0, sizeof(float) * KRENGINE_AUDIO_BLOCK_LENGTH); + + std::set active_sources = m_activeAudioSources; + + for(std::set::iterator itr=active_sources.begin(); itr != active_sources.end(); itr++) { + KRAudioSource *source = *itr; + if(&source->getScene() == m_listener_scene) { + float containment_factor = 0.0f; + + for(unordered_map::iterator zone_itr=m_reverb_zone_weights.begin(); zone_itr != m_reverb_zone_weights.end(); zone_itr++) { + siren_reverb_zone_weight_info zi = (*zone_itr).second; + float gain = zi.weight * zi.reverb_zone->getReverbGain() * zi.reverb_zone->getContainment(source->getWorldTranslation()); + if(gain > containment_factor) containment_factor = gain; + } + + + float reverb_send_level = m_global_reverb_send_level * m_global_gain * source->getReverb() * containment_factor; + if(reverb_send_level > 0.0f) { + source->sample(KRENGINE_AUDIO_BLOCK_LENGTH, 0, reverb_data, reverb_send_level); + KRDSP::Accumulate(reverb_accum, 1, reverb_data, 1, KRENGINE_AUDIO_BLOCK_LENGTH); + } + } + } + + // Apply impulse response reverb + // KRAudioSample *impulse_response = getContext().getAudioManager()->get("hrtf_kemar_H10e040a"); + + int impulse_response_blocks = 0; + for(unordered_map::iterator zone_itr=m_reverb_zone_weights.begin(); zone_itr != m_reverb_zone_weights.end(); zone_itr++) { + siren_reverb_zone_weight_info zi = (*zone_itr).second; + if(zi.reverb_sample) { + int zone_sample_blocks = KRMIN(zi.reverb_sample->getFrameCount(), (int)(m_reverb_max_length * 44100.0f)) / KRENGINE_AUDIO_BLOCK_LENGTH + 1; + impulse_response_blocks = KRMAX(impulse_response_blocks, zone_sample_blocks); + } + } + +// KRAudioSample *impulse_response_sample = getContext().getAudioManager()->get("test_reverb"); + if(impulse_response_blocks) { +// int impulse_response_blocks = impulse_response_sample->getFrameCount() / KRENGINE_AUDIO_BLOCK_LENGTH + 1; + int period_log2 = 0; + int impulse_response_block=0; + int period_count = 0; + + while(impulse_response_block < impulse_response_blocks) { + int period = 1 << period_log2; + + if((m_reverb_sequence + period - period_count) % period == 0) { + renderReverbImpulseResponse(impulse_response_block * KRENGINE_AUDIO_BLOCK_LENGTH, KRENGINE_AUDIO_BLOCK_LOG2N + period_log2); + } + + impulse_response_block += period; + + period_count++; + if(period_count >= period) { + period_count = 0; + if(KRENGINE_AUDIO_BLOCK_LOG2N + period_log2 + 1 < KRENGINE_REVERB_MAX_FFT_LOG2) { // FFT Size is double the number of frames in the period + period_log2++; + } + } + } + } + + m_reverb_sequence = (m_reverb_sequence + 1) % 0x1000000; + + // Rotate reverb buffer + m_reverb_input_next_sample = (m_reverb_input_next_sample + KRENGINE_AUDIO_BLOCK_LENGTH) % KRENGINE_REVERB_MAX_SAMPLES; +} + +void KRAudioManager::renderBlock() +{ + m_mutex.lock(); + + // ----====---- Advance to next block in accumulation buffer ----====---- + + // Zero out block that was last used, so it will be ready for the next pass through the circular buffer + float *block_data = getBlockAddress(0); + memset(block_data, 0, KRENGINE_AUDIO_BLOCK_LENGTH * KRENGINE_MAX_OUTPUT_CHANNELS * sizeof(float)); + + // Advance to the next block, and wrap around + m_output_accumulation_block_start = (m_output_accumulation_block_start + KRENGINE_AUDIO_BLOCK_LENGTH * KRENGINE_MAX_OUTPUT_CHANNELS) % (KRENGINE_REVERB_MAX_SAMPLES * KRENGINE_MAX_OUTPUT_CHANNELS); + + if(m_enable_audio) { + // ----====---- Render Direct / HRTF audio ----====---- + if(m_enable_hrtf) { + renderHRTF(); + } else { + renderITD(); + } + + // ----====---- Render Indirect / Reverb channel ----====---- + if(m_enable_reverb && m_reverb_max_length > 0.0f) { + renderReverb(); + } + + // ----====---- Render Ambient Sound ----====---- + renderAmbient(); + + // ----====---- Render Ambient Sound ----====---- + renderLimiter(); + } + + // ----====---- Advance audio sources ----====---- + m_audio_frame += KRENGINE_AUDIO_BLOCK_LENGTH; + + std::set open_samples = m_openAudioSamples; + + for(auto itr=open_samples.begin(); itr != open_samples.end(); itr++) { + KRAudioSample *sample = *itr; + sample->_endFrame(); + } + + m_anticlick_block = false; + m_mutex.unlock(); +} + +#ifdef __APPLE__ +// Apple Core Audio + +// audio render procedure, don't allocate memory, don't take any locks, don't waste time +OSStatus KRAudioManager::renderInput(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData) +{ + ((KRAudioManager*)inRefCon)->renderAudio(inNumberFrames, ioData); + return noErr; +} + +void KRSetAUCanonical(AudioStreamBasicDescription &desc, UInt32 nChannels, bool interleaved) +{ + desc.mFormatID = kAudioFormatLinearPCM; + desc.mFormatFlags = kAudioFormatFlagsNativeFloatPacked; + desc.mChannelsPerFrame = nChannels; + desc.mFramesPerPacket = 1; + desc.mBitsPerChannel = 8 * sizeof(Float32); + if (interleaved) + desc.mBytesPerPacket = desc.mBytesPerFrame = nChannels * sizeof(Float32); + else { + desc.mBytesPerPacket = desc.mBytesPerFrame = sizeof(Float32); + desc.mFormatFlags |= kAudioFormatFlagIsNonInterleaved; + } +} +#endif // Apple Core Audio + +void KRAudioManager::initHRTF() +{ + m_hrtf_sample_locations.push_back(Vector2::Create(-10.0f,000.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-10.0f,005.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-10.0f,010.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-10.0f,015.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-10.0f,020.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-10.0f,025.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-10.0f,030.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-10.0f,035.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-10.0f,040.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-10.0f,045.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-10.0f,050.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-10.0f,055.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-10.0f,060.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-10.0f,065.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-10.0f,070.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-10.0f,075.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-10.0f,080.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-10.0f,085.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-10.0f,090.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-10.0f,095.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-10.0f,100.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-10.0f,105.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-10.0f,110.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-10.0f,115.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-10.0f,120.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-10.0f,125.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-10.0f,130.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-10.0f,135.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-10.0f,140.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-10.0f,145.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-10.0f,150.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-10.0f,155.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-10.0f,160.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-10.0f,165.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-10.0f,170.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-10.0f,175.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-10.0f,180.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-20.0f,000.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-20.0f,005.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-20.0f,010.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-20.0f,015.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-20.0f,020.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-20.0f,025.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-20.0f,030.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-20.0f,035.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-20.0f,040.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-20.0f,045.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-20.0f,050.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-20.0f,055.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-20.0f,060.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-20.0f,065.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-20.0f,070.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-20.0f,075.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-20.0f,080.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-20.0f,085.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-20.0f,090.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-20.0f,095.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-20.0f,100.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-20.0f,105.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-20.0f,110.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-20.0f,115.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-20.0f,120.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-20.0f,125.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-20.0f,130.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-20.0f,135.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-20.0f,140.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-20.0f,145.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-20.0f,150.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-20.0f,155.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-20.0f,160.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-20.0f,165.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-20.0f,170.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-20.0f,175.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-20.0f,180.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-30.0f,000.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-30.0f,006.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-30.0f,012.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-30.0f,018.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-30.0f,024.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-30.0f,030.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-30.0f,036.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-30.0f,042.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-30.0f,048.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-30.0f,054.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-30.0f,060.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-30.0f,066.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-30.0f,072.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-30.0f,078.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-30.0f,084.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-30.0f,090.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-30.0f,096.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-30.0f,102.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-30.0f,108.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-30.0f,114.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-30.0f,120.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-30.0f,126.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-30.0f,132.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-30.0f,138.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-30.0f,144.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-30.0f,150.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-30.0f,156.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-30.0f,162.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-30.0f,168.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-30.0f,174.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-30.0f,180.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-40.0f,000.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-40.0f,006.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-40.0f,013.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-40.0f,019.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-40.0f,026.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-40.0f,032.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-40.0f,039.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-40.0f,045.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-40.0f,051.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-40.0f,058.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-40.0f,064.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-40.0f,071.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-40.0f,077.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-40.0f,084.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-40.0f,090.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-40.0f,096.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-40.0f,103.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-40.0f,109.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-40.0f,116.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-40.0f,122.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-40.0f,129.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-40.0f,135.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-40.0f,141.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-40.0f,148.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-40.0f,154.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-40.0f,161.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-40.0f,167.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-40.0f,174.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(-40.0f,180.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(0.0f,000.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(0.0f,005.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(0.0f,010.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(0.0f,015.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(0.0f,020.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(0.0f,025.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(0.0f,030.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(0.0f,035.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(0.0f,040.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(0.0f,045.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(0.0f,050.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(0.0f,055.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(0.0f,060.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(0.0f,065.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(0.0f,070.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(0.0f,075.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(0.0f,080.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(0.0f,085.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(0.0f,090.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(0.0f,095.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(0.0f,100.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(0.0f,105.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(0.0f,110.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(0.0f,115.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(0.0f,120.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(0.0f,125.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(0.0f,130.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(0.0f,135.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(0.0f,140.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(0.0f,145.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(0.0f,150.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(0.0f,155.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(0.0f,160.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(0.0f,165.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(0.0f,170.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(0.0f,175.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(0.0f,180.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(10.0f,000.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(10.0f,005.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(10.0f,010.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(10.0f,015.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(10.0f,020.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(10.0f,025.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(10.0f,030.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(10.0f,035.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(10.0f,040.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(10.0f,045.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(10.0f,050.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(10.0f,055.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(10.0f,060.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(10.0f,065.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(10.0f,070.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(10.0f,075.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(10.0f,080.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(10.0f,085.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(10.0f,090.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(10.0f,095.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(10.0f,100.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(10.0f,105.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(10.0f,110.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(10.0f,115.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(10.0f,120.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(10.0f,125.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(10.0f,130.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(10.0f,135.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(10.0f,140.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(10.0f,145.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(10.0f,150.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(10.0f,155.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(10.0f,160.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(10.0f,165.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(10.0f,170.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(10.0f,175.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(10.0f,180.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(20.0f,000.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(20.0f,005.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(20.0f,010.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(20.0f,015.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(20.0f,020.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(20.0f,025.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(20.0f,030.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(20.0f,035.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(20.0f,040.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(20.0f,045.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(20.0f,050.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(20.0f,055.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(20.0f,060.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(20.0f,065.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(20.0f,070.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(20.0f,075.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(20.0f,080.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(20.0f,085.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(20.0f,090.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(20.0f,095.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(20.0f,100.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(20.0f,105.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(20.0f,110.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(20.0f,115.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(20.0f,120.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(20.0f,125.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(20.0f,130.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(20.0f,135.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(20.0f,140.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(20.0f,145.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(20.0f,150.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(20.0f,155.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(20.0f,160.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(20.0f,165.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(20.0f,170.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(20.0f,175.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(20.0f,180.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(30.0f,000.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(30.0f,006.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(30.0f,012.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(30.0f,018.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(30.0f,024.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(30.0f,030.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(30.0f,036.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(30.0f,042.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(30.0f,048.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(30.0f,054.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(30.0f,060.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(30.0f,066.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(30.0f,072.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(30.0f,078.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(30.0f,084.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(30.0f,090.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(30.0f,096.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(30.0f,102.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(30.0f,108.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(30.0f,114.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(30.0f,120.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(30.0f,126.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(30.0f,132.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(30.0f,138.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(30.0f,144.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(30.0f,150.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(30.0f,156.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(30.0f,162.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(30.0f,168.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(30.0f,174.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(30.0f,180.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(40.0f,000.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(40.0f,006.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(40.0f,013.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(40.0f,019.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(40.0f,026.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(40.0f,032.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(40.0f,039.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(40.0f,045.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(40.0f,051.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(40.0f,058.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(40.0f,064.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(40.0f,071.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(40.0f,077.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(40.0f,084.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(40.0f,090.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(40.0f,096.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(40.0f,103.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(40.0f,109.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(40.0f,116.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(40.0f,122.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(40.0f,129.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(40.0f,135.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(40.0f,141.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(40.0f,148.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(40.0f,154.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(40.0f,161.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(40.0f,167.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(40.0f,174.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(40.0f,180.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(50.0f,000.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(50.0f,008.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(50.0f,016.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(50.0f,024.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(50.0f,032.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(50.0f,040.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(50.0f,048.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(50.0f,056.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(50.0f,064.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(50.0f,072.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(50.0f,080.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(50.0f,088.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(50.0f,096.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(50.0f,104.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(50.0f,112.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(50.0f,120.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(50.0f,128.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(50.0f,136.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(50.0f,144.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(50.0f,152.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(50.0f,160.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(50.0f,168.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(50.0f,176.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(60.0f,000.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(60.0f,010.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(60.0f,020.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(60.0f,030.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(60.0f,040.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(60.0f,050.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(60.0f,060.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(60.0f,070.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(60.0f,080.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(60.0f,090.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(60.0f,100.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(60.0f,110.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(60.0f,120.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(60.0f,130.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(60.0f,140.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(60.0f,150.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(60.0f,160.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(60.0f,170.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(60.0f,180.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(70.0f,000.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(70.0f,015.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(70.0f,030.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(70.0f,045.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(70.0f,060.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(70.0f,075.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(70.0f,090.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(70.0f,105.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(70.0f,120.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(70.0f,135.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(70.0f,150.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(70.0f,165.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(70.0f,180.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(80.0f,000.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(80.0f,030.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(80.0f,060.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(80.0f,090.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(80.0f,120.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(80.0f,150.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(80.0f,180.0f)); + m_hrtf_sample_locations.push_back(Vector2::Create(90.0f,000.0f)); + + if(m_hrtf_data) { + delete m_hrtf_data; + } + m_hrtf_data = (float *)malloc(m_hrtf_sample_locations.size() * sizeof(float) * 128 * 4 * 2); + + for(int channel=0; channel < 2; channel++) { + m_hrtf_spectral[channel].clear(); + } + + int sample_index=0; + for(std::vector::iterator itr=m_hrtf_sample_locations.begin(); itr != m_hrtf_sample_locations.end(); itr++) { + Vector2 pos = *itr; + KRAudioSample *sample = getHRTFSample(pos); + for(int channel=0; channel < 2; channel++) { + KRDSP::SplitComplex spectral; + spectral.realp = m_hrtf_data + sample_index * 1024 + channel * 512; + spectral.imagp = m_hrtf_data + sample_index * 1024 + channel * 512 + 256; + sample->sample(0, 128, channel, spectral.realp, 1.0f, false); + memset(spectral.realp + 128, 0, sizeof(float) * 128); + memset(spectral.imagp, 0, sizeof(float) * 256); + KRDSP::FFTForward(m_fft_setup[8 - KRENGINE_AUDIO_BLOCK_LOG2N], &spectral, 8); + m_hrtf_spectral[channel][pos] = spectral; + } + sample_index++; + } + +} + +KRAudioSample *KRAudioManager::getHRTFSample(const Vector2 &hrtf_dir) +{ + //hrtf_kemar_H-10e000a.wav + char szName[64]; + sprintf(szName, "hrtf_kemar_H%de%03da", int(hrtf_dir.x), abs(int(hrtf_dir.y))); + + return get(szName); +} + +KRDSP::SplitComplex KRAudioManager::getHRTFSpectral(const Vector2 &hrtf_dir, const int channel) +{ + Vector2 dir = hrtf_dir; + int sample_channel = channel; + if(dir.y < 0) { + dir.y = -dir.y; + sample_channel = (channel + 1) % 2; + } + return m_hrtf_spectral[sample_channel][dir]; +} + +Vector2 KRAudioManager::getNearestHRTFSample(const Vector2 &dir) +{ + float elev_gran = 10.0f; + + + Vector2 dir_deg = dir * (180.0f / M_PI); + float elevation = floor(dir_deg.x / elev_gran + 0.5f) * elev_gran; + if(elevation < -40.0f) { + elevation = -40.0f; + } + + Vector2 min_direction; + bool first = true; + float min_distance = 360.0f; + for(std::vector::iterator itr = m_hrtf_sample_locations.begin(); itr != m_hrtf_sample_locations.end(); itr++) { + if(first) { + first = false; + min_direction = (*itr); + } else if((*itr).x == elevation) { + float distance = fabs(dir_deg.y - (*itr).y); + if(min_distance > distance) { + min_direction = (*itr); + min_distance = distance; + } + distance = fabs(dir_deg.y - -(*itr).y); + if(min_distance > distance) { + min_direction = (*itr); + min_direction.y = -min_direction.y; + min_distance = distance; + } + } + } + return min_direction; +} + +void KRAudioManager::getHRTFMix(const Vector2 &dir, Vector2 &dir1, Vector2 &dir2, Vector2 &dir3, Vector2 &dir4, float &mix1, float &mix2, float &mix3, float &mix4) +{ + float elev_gran = 10.0f; + + float elevation = dir.x * 180.0f / M_PI; + float azimuth = dir.y * 180.0f / M_PI; + + float elev1 = floor(elevation / elev_gran) * elev_gran; + float elev2 = ceil(elevation / elev_gran) * elev_gran; + float elev_blend = (elevation - elev1) / elev_gran; + if(elev1 < -40.0f) { + elev1 = -40.0f; + elev2 = -40.0f; + elev_blend = 0.0f; + } + + dir1.x = elev1; + dir2.x = elev1; + dir3.x = elev2; + dir4.x = elev2; + + bool first1 = true; + bool first2 = true; + bool first3 = true; + bool first4 = true; + for(std::vector::iterator itr = m_hrtf_sample_locations.begin(); itr != m_hrtf_sample_locations.end(); itr++) { + Vector2 sample_pos = *itr; + + if(sample_pos.x == elev1) { + if(sample_pos.y <= azimuth) { + if(first1) { + first1 = false; + dir1.y = sample_pos.y; + } else if(sample_pos.y > dir1.y) { + dir1.y = sample_pos.y; + } + } + if(-sample_pos.y <= azimuth) { + if(first1) { + first1 = false; + dir1.y = -sample_pos.y; + } else if(-sample_pos.y > dir1.y) { + dir1.y = -sample_pos.y; + } + } + + if(sample_pos.y > azimuth) { + if(first2) { + first2 = false; + dir2.y = sample_pos.y; + } else if(sample_pos.y < dir2.y) { + dir2.y = sample_pos.y; + } + } + if(-sample_pos.y > azimuth) { + if(first2) { + first2 = false; + dir2.y = -sample_pos.y; + } else if(-sample_pos.y < dir2.y) { + dir2.y = -sample_pos.y; + } + } + } + if(sample_pos.x == elev2) { + if(sample_pos.y <= azimuth) { + if(first3) { + first3 = false; + dir3.y = sample_pos.y; + } else if(sample_pos.y > dir3.y) { + dir3.y = sample_pos.y; + } + } + if(-sample_pos.y <= azimuth) { + if(first3) { + first3 = false; + dir3.y = -sample_pos.y; + } else if(-sample_pos.y > dir3.y) { + dir3.y = -sample_pos.y; + } + } + + if(sample_pos.y > azimuth) { + if(first4) { + first4 = false; + dir4.y = sample_pos.y; + } else if(sample_pos.y < dir4.y) { + dir4.y = sample_pos.y; + } + } + if(-sample_pos.y > azimuth) { + if(first4) { + first4 = false; + dir4.y = -sample_pos.y; + } else if(-sample_pos.y < dir4.y) { + dir4.y = -sample_pos.y; + } + } + } + } + + + float azim_blend1 = 0.0f; + if(dir2.y > dir1.y) { + azim_blend1 = (azimuth - dir1.y) / (dir2.y - dir1.y); + } + + float azim_blend2 = 0.0f; + if(dir4.y > dir3.y) { + azim_blend2 = (azimuth - dir3.y) / (dir4.y - dir3.y); + } + + mix1 = (1.0f - azim_blend1) * (1.0f - elev_blend); + mix2 = azim_blend1 * (1.0f - elev_blend); + mix3 = (1.0f - azim_blend2) * elev_blend; + mix4 = azim_blend2 * elev_blend; +} + +void KRAudioManager::initAudio() +{ + if(!m_initialized) { + m_initialized = true; + m_output_sample = KRENGINE_AUDIO_BLOCK_LENGTH; + + // initialize double-buffer for reverb input + int buffer_size = sizeof(float) * KRENGINE_REVERB_MAX_SAMPLES; // Reverb input is a single channel, circular buffered + m_reverb_input_samples = (float *)malloc(buffer_size); + memset(m_reverb_input_samples, 0, buffer_size); + m_reverb_input_next_sample = 0; + + m_output_accumulation = (float *)malloc(buffer_size * 2); // 2 channels + memset(m_output_accumulation, 0, buffer_size * 2); + m_output_accumulation_block_start = 0; + + m_workspace_data = (float *)malloc(KRENGINE_REVERB_WORKSPACE_SIZE * 6 * sizeof(float)); + m_workspace[0].realp = m_workspace_data + KRENGINE_REVERB_WORKSPACE_SIZE * 0; + m_workspace[0].imagp = m_workspace_data + KRENGINE_REVERB_WORKSPACE_SIZE * 1; + m_workspace[1].realp = m_workspace_data + KRENGINE_REVERB_WORKSPACE_SIZE * 2; + m_workspace[1].imagp = m_workspace_data + KRENGINE_REVERB_WORKSPACE_SIZE * 3; + m_workspace[2].realp = m_workspace_data + KRENGINE_REVERB_WORKSPACE_SIZE * 4; + m_workspace[2].imagp = m_workspace_data + KRENGINE_REVERB_WORKSPACE_SIZE * 5; + + m_reverb_sequence = 0; + for(int i=KRENGINE_AUDIO_BLOCK_LOG2N; i <= KRENGINE_REVERB_MAX_FFT_LOG2; i++) { + m_fft_setup[i - KRENGINE_AUDIO_BLOCK_LOG2N].create(i); + // FINDME, TODO.. Apple's vDSP only needs one + // KRDSP::FFTWorkspace, initialized with the maximum size + } + + // ----====---- Initialize HRTF Engine ----====---- + initHRTF(); + +#ifdef __APPLE__ + // Apple Core Audio + // ----====---- Initialize Core Audio Objects ----====---- + OSDEBUG(NewAUGraph(&m_auGraph)); + + // ---- Create output node ---- + AudioComponentDescription output_desc; + output_desc.componentType = kAudioUnitType_Output; +#if TARGET_OS_IPHONE + output_desc.componentSubType = kAudioUnitSubType_RemoteIO; +#else + output_desc.componentSubType = kAudioUnitSubType_DefaultOutput; +#endif + output_desc.componentFlags = 0; + output_desc.componentFlagsMask = 0; + output_desc.componentManufacturer = kAudioUnitManufacturer_Apple; + AUNode outputNode = 0; + OSDEBUG(AUGraphAddNode(m_auGraph, &output_desc, &outputNode)); + + // ---- Create mixer node ---- + AudioComponentDescription mixer_desc; + mixer_desc.componentType = kAudioUnitType_Mixer; + mixer_desc.componentSubType = kAudioUnitSubType_MultiChannelMixer; + mixer_desc.componentFlags = 0; + mixer_desc.componentFlagsMask = 0; + mixer_desc.componentManufacturer = kAudioUnitManufacturer_Apple; + AUNode mixerNode = 0; + OSDEBUG(AUGraphAddNode(m_auGraph, &mixer_desc, &mixerNode )); + + // ---- Connect mixer to output node ---- + OSDEBUG(AUGraphConnectNodeInput(m_auGraph, mixerNode, 0, outputNode, 0)); + + // ---- Open the audio graph ---- + OSDEBUG(AUGraphOpen(m_auGraph)); + + // ---- Get a handle to the mixer ---- + OSDEBUG(AUGraphNodeInfo(m_auGraph, mixerNode, NULL, &m_auMixer)); + + // ---- Add output channel to mixer ---- + UInt32 bus_count = 1; + OSDEBUG(AudioUnitSetProperty(m_auMixer, kAudioUnitProperty_ElementCount, kAudioUnitScope_Input, 0, &bus_count, sizeof(bus_count))); + + // ---- Attach render function to channel ---- + AURenderCallbackStruct renderCallbackStruct; + renderCallbackStruct.inputProc = &renderInput; + renderCallbackStruct.inputProcRefCon = this; + OSDEBUG(AUGraphSetNodeInputCallback(m_auGraph, mixerNode, 0, &renderCallbackStruct)); // 0 = mixer input number + + AudioStreamBasicDescription desc; + memset(&desc, 0, sizeof(desc)); + + UInt32 size = sizeof(desc); + memset(&desc, 0, sizeof(desc)); + OSDEBUG(AudioUnitGetProperty( m_auMixer, + kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Input, + 0, // 0 = mixer input number + &desc, + &size)); + + KRSetAUCanonical(desc, 2, false); + desc.mSampleRate = 44100.0f; + + OSDEBUG(AudioUnitSetProperty(m_auMixer, + kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Input, + 0, // 0 == mixer input number + &desc, + sizeof(desc))); + + // ---- Apply properties to mixer output ---- + OSDEBUG(AudioUnitSetProperty(m_auMixer, + kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Output, + 0, // Always 0 for output bus + &desc, + sizeof(desc))); + + + memset(&desc, 0, sizeof(desc)); + size = sizeof(desc); + OSDEBUG(AudioUnitGetProperty(m_auMixer, + kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Output, + 0, + &desc, + &size)); + + // ---- + KRSetAUCanonical(desc, 2, false); + desc.mSampleRate = 44100.0f; + + + // ---- + + OSDEBUG(AudioUnitSetProperty(m_auMixer, + kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Output, + 0, + &desc, + sizeof(desc))); + + + OSDEBUG(AudioUnitSetParameter(m_auMixer, kMultiChannelMixerParam_Volume, kAudioUnitScope_Input, 0, 1.0, 0)); + OSDEBUG(AudioUnitSetParameter(m_auMixer, kMultiChannelMixerParam_Volume, kAudioUnitScope_Output, 0, 1.0, 0)); + + OSDEBUG(AUGraphInitialize(m_auGraph)); + + // ----====---- Start the audio system ----====---- + OSDEBUG(AUGraphStart(m_auGraph)); + +// CAShow(m_auGraph); +#endif // Core Audio + } +} + + +void KRAudioManager::cleanupAudio() +{ +#ifdef __APPLE__ + // Apple Core Audio + if(m_auGraph) { + OSDEBUG(AUGraphStop(m_auGraph)); + OSDEBUG(DisposeAUGraph(m_auGraph)); + m_auGraph = NULL; + m_auMixer = NULL; + } +#endif + + if(m_reverb_input_samples) { + free(m_reverb_input_samples); + m_reverb_input_samples = NULL; + } + + if(m_output_accumulation) { + free(m_output_accumulation); + m_output_accumulation = NULL; + } + + if(m_workspace_data) { + free(m_workspace_data); + m_workspace_data = NULL; + } + + if(m_hrtf_data) { + free(m_hrtf_data); + m_hrtf_data = NULL; + } + + for(int i=0; i < KRENGINE_MAX_REVERB_IMPULSE_MIX; i++) { + m_reverb_impulse_responses[i] = NULL; + m_reverb_impulse_responses_weight[i] = 0.0f; + } + + for(int i=KRENGINE_AUDIO_BLOCK_LOG2N; i <= KRENGINE_REVERB_MAX_FFT_LOG2; i++) { + m_fft_setup[i - KRENGINE_AUDIO_BLOCK_LOG2N].destroy(); + } +} + +KRAudioManager::~KRAudioManager() +{ + for(unordered_map::iterator name_itr=m_sounds.begin(); name_itr != m_sounds.end(); name_itr++) { + delete (*name_itr).second; + } + + cleanupAudio(); + + for(std::vector::iterator itr = m_bufferPoolIdle.begin(); itr != m_bufferPoolIdle.end(); itr++) { + delete *itr; + } +} + +void KRAudioManager::makeCurrentContext() +{ + initAudio(); +} + +void KRAudioManager::setListenerOrientationFromModelMatrix(const Matrix4 &modelMatrix) +{ + setListenerOrientation( + Matrix4::Dot(modelMatrix, Vector3::Create(0.0, 0.0, 0.0)), + Vector3::Normalize(Matrix4::Dot(modelMatrix, Vector3::Create(0.0, 0.0, -1.0)) - m_listener_position), + Vector3::Normalize(Matrix4::Dot(modelMatrix, Vector3::Create(0.0, 1.0, 0.0)) - m_listener_position) + ); +} + +Vector3 &KRAudioManager::getListenerForward() +{ + return m_listener_forward; +} + +Vector3 &KRAudioManager::getListenerPosition() +{ + return m_listener_position; +} + +Vector3 &KRAudioManager::getListenerUp() +{ + return m_listener_up; +} + +void KRAudioManager::setListenerOrientation(const Vector3 &position, const Vector3 &forward, const Vector3 &up) +{ + m_listener_position = position; + m_listener_forward = forward; + m_listener_up = up; + + makeCurrentContext(); +} + +void KRAudioManager::add(KRAudioSample *sound) +{ + std::string lower_name = sound->getName(); + std::transform(lower_name.begin(), lower_name.end(), lower_name.begin(), ::tolower); + + unordered_map::iterator name_itr = m_sounds.find(lower_name); + if(name_itr != m_sounds.end()) { + delete (*name_itr).second; + (*name_itr).second = sound; + } else { + m_sounds[lower_name] = sound; + } +} + +KRAudioSample *KRAudioManager::load(const std::string &name, const std::string &extension, KRDataBlock *data) +{ + KRAudioSample *Sound = new KRAudioSample(getContext(), name, extension, data); + if(Sound) add(Sound); + return Sound; +} + +KRAudioSample *KRAudioManager::get(const std::string &name) +{ + std::string lower_name = name; + std::transform(lower_name.begin(), lower_name.end(), lower_name.begin(), ::tolower); + return m_sounds[lower_name]; +} + +KRDataBlock *KRAudioManager::getBufferData(int size) +{ + KRDataBlock *data; + // Note: We only store and recycle buffers with a size of CIRCA_AUDIO_MAX_BUFFER_SIZE + if(size == KRENGINE_AUDIO_MAX_BUFFER_SIZE && m_bufferPoolIdle.size() > 0) { + // Recycle a buffer from the pool + data = m_bufferPoolIdle.back(); + m_bufferPoolIdle.pop_back(); + } else { + data = new KRDataBlock(); + data->expand(size); + } + data->lock(); + return data; +} + +void KRAudioManager::recycleBufferData(KRDataBlock *data) +{ + if(data != NULL) { + data->unlock(); + if(data->getSize() == KRENGINE_AUDIO_MAX_BUFFER_SIZE && m_bufferPoolIdle.size() < KRENGINE_AUDIO_MAX_POOL_SIZE) { + m_bufferPoolIdle.push_back(data); + } else { + delete data; + } + } +} + +void KRAudioManager::activateAudioSource(KRAudioSource *audioSource) +{ + m_activeAudioSources.insert(audioSource); +} + +void KRAudioManager::deactivateAudioSource(KRAudioSource *audioSource) +{ + m_activeAudioSources.erase(audioSource); +} + +void KRAudioManager::_registerOpenAudioSample(KRAudioSample *audioSample) +{ + m_openAudioSamples.insert(audioSample); +} + +void KRAudioManager::_registerCloseAudioSample(KRAudioSample *audioSample) +{ + m_openAudioSamples.erase(audioSample); +} + +__int64_t KRAudioManager::getAudioFrame() +{ + return m_audio_frame; +} + +KRAudioBuffer *KRAudioManager::getBuffer(KRAudioSample &audio_sample, int buffer_index) +{ + // ----====---- Try to find the buffer in the cache ----====---- + for(std::vector::iterator itr=m_bufferCache.begin(); itr != m_bufferCache.end(); itr++) { + KRAudioBuffer *cache_buffer = *itr; + if(cache_buffer->getAudioSample() == &audio_sample && cache_buffer->getIndex() == buffer_index) { + return cache_buffer; + } + } + + // ----====---- Make room in the cache for a new buffer ----====---- + if(m_bufferCache.size() >= KRENGINE_AUDIO_MAX_POOL_SIZE) { + // delete a random entry from the cache +#ifdef __APPLE__ + int index_to_delete = arc4random() % m_bufferCache.size(); +#else + int index_to_delete = rand() % m_bufferCache.size(); +#endif + std::vector::iterator itr_to_delete = m_bufferCache.begin() + index_to_delete; + delete *itr_to_delete; + m_bufferCache.erase(itr_to_delete); + } + + // ----====---- Request new buffer, add to cache, and return it ----====---- + KRAudioBuffer *buffer = audio_sample.getBuffer(buffer_index); + m_bufferCache.push_back(buffer); + return buffer; +} + +float KRAudioManager::getGlobalReverbSendLevel() +{ + return m_global_reverb_send_level; +} + +void KRAudioManager::setGlobalReverbSendLevel(float send_level) +{ + m_global_reverb_send_level = send_level; +} + +float KRAudioManager::getGlobalGain() +{ + return m_global_gain; +} + +void KRAudioManager::setGlobalGain(float gain) +{ + m_global_gain = gain; +} + +float KRAudioManager::getGlobalAmbientGain() +{ + return m_global_ambient_gain; +} + +void KRAudioManager::setGlobalAmbientGain(float gain) +{ + m_global_ambient_gain = gain; +} + +void KRAudioManager::startFrame(float deltaTime) +{ + m_mutex.lock(); + + // ----====---- Determine Ambient Zone Contributions ----====---- + m_ambient_zone_weights.clear(); + m_ambient_zone_total_weight = 0.0f; // For normalizing zone weights + if(m_listener_scene) { + std::set ambient_zones = m_listener_scene->getAmbientZones(); + + for(std::set::iterator itr=ambient_zones.begin(); itr != ambient_zones.end(); itr++) { + KRAmbientZone *sphere = *itr; + siren_ambient_zone_weight_info zi; + + zi.weight = sphere->getContainment(m_listener_position); + if(zi.weight > 0.0f) { + if(m_ambient_zone_weights.find(sphere->getZone()) == m_ambient_zone_weights.end()) { + zi.ambient_zone = sphere; + zi.ambient_sample = get(sphere->getAmbient()); + m_ambient_zone_weights[sphere->getZone()] = zi; + m_ambient_zone_total_weight += zi.weight; + } else { + float prev_weight = m_ambient_zone_weights[sphere->getZone()].weight; + if(zi.weight > prev_weight) { + m_ambient_zone_total_weight -= prev_weight; + m_ambient_zone_weights[sphere->getZone()].weight = zi.weight; + m_ambient_zone_total_weight += zi.weight; + } + } + } + } + } + + // ----====---- Determine Reverb Zone Contributions ----====---- + m_reverb_zone_weights.clear(); + m_reverb_zone_total_weight = 0.0f; // For normalizing zone weights + if(m_listener_scene) { + std::set reverb_zones = m_listener_scene->getReverbZones(); + + for(std::set::iterator itr=reverb_zones.begin(); itr != reverb_zones.end(); itr++) { + KRReverbZone *sphere = *itr; + siren_reverb_zone_weight_info zi; + + zi.weight = sphere->getContainment(m_listener_position); + if(zi.weight > 0.0f) { + if(m_reverb_zone_weights.find(sphere->getZone()) == m_reverb_zone_weights.end()) { + zi.reverb_zone = sphere; + zi.reverb_sample = get(sphere->getReverb()); + m_reverb_zone_weights[sphere->getZone()] = zi; + m_reverb_zone_total_weight += zi.weight; + } else { + float prev_weight = m_reverb_zone_weights[sphere->getZone()].weight; + if(zi.weight > prev_weight) { + m_reverb_zone_total_weight -= prev_weight; + m_reverb_zone_weights[sphere->getZone()].weight = zi.weight; + m_reverb_zone_total_weight += zi.weight; + } + } + } + } + } + + + + // ----====---- Map Source Directions and Gains ----====---- + m_prev_mapped_sources.clear(); + m_mapped_sources.swap(m_prev_mapped_sources); + + Vector3 listener_right = Vector3::Cross(m_listener_forward, m_listener_up); + std::set active_sources = m_activeAudioSources; + + + for(std::set::iterator itr=active_sources.begin(); itr != active_sources.end(); itr++) { + KRAudioSource *source = *itr; + Vector3 source_world_position = source->getWorldTranslation(); + Vector3 diff = source_world_position - m_listener_position; + float distance = diff.magnitude(); + float gain = source->getGain() * m_global_gain / pow(KRMAX(distance / source->getReferenceDistance(), 1.0f), source->getRolloffFactor()); + + // apply minimum-cutoff so that we don't waste cycles processing very quiet / distant sound sources + gain = KRMAX(gain - KRENGINE_AUDIO_CUTOFF, 0.0f) / (1.0f - KRENGINE_AUDIO_CUTOFF); + + if(gain > 0.0f) { + + Vector3 source_listener_space = Vector3::Create( + Vector3::Dot(listener_right, diff), + Vector3::Dot(m_listener_up, diff), + Vector3::Dot(m_listener_forward, diff) + ); + + + Vector3 source_dir = Vector3::Normalize(source_listener_space); + + + + if(source->getEnableOcclusion() && /* FINDME!! DISABLES CODE */ (false)) { + HitInfo hitinfo; + if(source->getScene().lineCast(m_listener_position, source_world_position, hitinfo, KRAKEN_COLLIDER_AUDIO)) { + gain = 0.0f; + } + } + + Vector2 source_dir2 = Vector2::Normalize(Vector2::Create(source_dir.x, source_dir.z)); + float azimuth = -atan2(source_dir2.x, -source_dir2.y); + float elevation = atan( source_dir.y / sqrt(source_dir.x * source_dir.x + source_dir.z * source_dir.z)); + + Vector2 adjusted_source_dir = Vector2::Create(elevation, azimuth); + + if(!m_high_quality_hrtf) { + adjusted_source_dir = getNearestHRTFSample(adjusted_source_dir); + } + + // Click Removal - Add ramping of gain changes for audio sources that are continuing to play + float gain_anticlick = 0.0f; + std::pair > >::iterator, unordered_multimap > >::iterator> prev_range = m_prev_mapped_sources.equal_range(adjusted_source_dir); + for(unordered_multimap > >::iterator prev_itr=prev_range.first; prev_itr != prev_range.second; prev_itr++) { + if( (*prev_itr).second.first == source) { + gain_anticlick = (*prev_itr).second.second.second; + break; + } + } + + m_mapped_sources.insert(std::pair > >(adjusted_source_dir, std::pair >(source, std::pair(gain_anticlick, gain)))); + } + } + + // Click Removal - Map audio sources for ramp-down of gain for audio sources that have been squelched by attenuation + for(unordered_multimap > >::iterator itr=m_prev_mapped_sources.begin(); itr != m_prev_mapped_sources.end(); itr++) { + + KRAudioSource *source = (*itr).second.first; + float source_prev_gain = (*itr).second.second.second; + if(source->isPlaying() && source_prev_gain > 0.0f) { + // Only create ramp-down channels for 3d sources that have been squelched by attenuation; this is not necessary if the sample has completed playing + Vector2 source_position = (*itr).first; + + std::pair > >::iterator, unordered_multimap > >::iterator> new_range = m_mapped_sources.equal_range(source_position); + bool already_merged = false; + for(unordered_multimap > >::iterator new_itr=new_range.first; new_itr != new_range.second; new_itr++) { + if( (*new_itr).second.first == source) { + already_merged = true; + break; + } + } + if(!already_merged) { + + // source gain becomes anticlick gain and gain becomes 0 for anti-click ramp-down. + m_mapped_sources.insert(std::pair > >(source_position, std::pair >(source, std::pair(source_prev_gain, 0.0f)))); + } + } + } + + m_anticlick_block = true; + m_mutex.unlock(); +} + +void KRAudioManager::renderAmbient() +{ + if(m_listener_scene) { + + int output_offset = (m_output_accumulation_block_start) % (KRENGINE_REVERB_MAX_SAMPLES * KRENGINE_MAX_OUTPUT_CHANNELS); + float *buffer = m_workspace[0].realp; + + for(unordered_map::iterator zone_itr=m_ambient_zone_weights.begin(); zone_itr != m_ambient_zone_weights.end(); zone_itr++) { + siren_ambient_zone_weight_info zi = (*zone_itr).second; + float gain = (*zone_itr).second.weight * zi.ambient_zone->getAmbientGain() * m_global_ambient_gain * m_global_gain; + + KRAudioSample *source_sample = zi.ambient_sample; + if(source_sample) { + for(int channel=0; channel < KRENGINE_MAX_OUTPUT_CHANNELS; channel++) { + source_sample->sample(getContext().getAudioManager()->getAudioFrame(), KRENGINE_AUDIO_BLOCK_LENGTH, channel, buffer, gain, true); + KRDSP::Accumulate(m_output_accumulation + output_offset + channel, KRENGINE_MAX_OUTPUT_CHANNELS, + buffer, 1, + KRENGINE_AUDIO_BLOCK_LENGTH); + } + } + } + } +} + +void KRAudioManager::renderHRTF() +{ + KRDSP::SplitComplex *hrtf_accum = m_workspace + 0; + KRDSP::SplitComplex *hrtf_impulse = m_workspace + 1; + KRDSP::SplitComplex *hrtf_convolved = m_workspace + 1; // We only need hrtf_impulse or hrtf_convolved at once; we can recycle the buffer + KRDSP::SplitComplex *hrtf_sample = m_workspace + 2; + + int impulse_response_channels = 2; + int hrtf_frames = 128; + int fft_size = 256; + int fft_size_log2 = 8; + + for(int channel=0; channel > >::iterator itr=m_mapped_sources.begin(); + while(itr != m_mapped_sources.end()) { + // Batch together sound sources that are emitted from the same direction + Vector2 source_direction = (*itr).first; + KRAudioSource *source = (*itr).second.first; + float gain_anticlick = (*itr).second.second.first; + float gain = (*itr).second.second.second; + + + // If this is the first or only sample, write directly to the first half of the FFT input buffer + // Subsequent samples write to the second half of the FFT input buffer, which is then added to the first half (the second half will be zero'ed out anyways and works as a convenient temporary buffer) + float *sample_buffer = first_source ? hrtf_sample->realp : hrtf_sample->realp + KRENGINE_AUDIO_BLOCK_LENGTH; + + if(gain != gain_anticlick && m_anticlick_block) { + // Sample and perform anti-click filtering + source->sample(KRENGINE_AUDIO_BLOCK_LENGTH, 0, sample_buffer, 1.0); + float ramp_gain = gain_anticlick; + float ramp_step = (gain - gain_anticlick) / KRENGINE_AUDIO_ANTICLICK_SAMPLES; + KRDSP::ScaleRamp(sample_buffer, ramp_gain, ramp_step, KRENGINE_AUDIO_ANTICLICK_SAMPLES); + if(KRENGINE_AUDIO_BLOCK_LENGTH > KRENGINE_AUDIO_ANTICLICK_SAMPLES) { + KRDSP::Scale(sample_buffer + KRENGINE_AUDIO_ANTICLICK_SAMPLES, gain, KRENGINE_AUDIO_BLOCK_LENGTH - KRENGINE_AUDIO_ANTICLICK_SAMPLES); + } + } else { + // Don't need to perform anti-click filtering, so just sample + source->sample(KRENGINE_AUDIO_BLOCK_LENGTH, 0, sample_buffer, gain); + } + + if(first_source) { + first_source = false; + } else { + // Accumulate samples on subsequent sources + KRDSP::Accumulate(hrtf_sample->realp, 1, sample_buffer, 1, KRENGINE_AUDIO_BLOCK_LENGTH); + } + + itr++; + + bool end_of_group = false; + if(itr == m_mapped_sources.end()) { + end_of_group = true; + } else { + Vector2 next_direction = (*itr).first; + end_of_group = next_direction != source_direction; + } + + if(end_of_group) { + // ----====---- We have reached the end of a batch; convolve with HRTF impulse response and add to output accumulation buffer ----====---- + first_source = true; + + memset(hrtf_sample->realp + hrtf_frames, 0, sizeof(float) * hrtf_frames); + memset(hrtf_sample->imagp, 0, sizeof(float) * fft_size); + + KRDSP::SplitComplex hrtf_spectral; + + if(m_high_quality_hrtf) { + // High quality, interpolated HRTF + hrtf_spectral = *hrtf_accum; + + float mix[4]; + Vector2 dir[4]; + + getHRTFMix(source_direction, dir[0], dir[1], dir[2], dir[3], mix[0], mix[1], mix[2], mix[3]); + + + memset(hrtf_accum->realp, 0, sizeof(float) * fft_size); + memset(hrtf_accum->imagp, 0, sizeof(float) * fft_size); + + for(int i=0; i < 1 /*4 */; i++) { + if(mix[i] > 0.0f) { + KRDSP::SplitComplex hrtf_impulse_sample = getHRTFSpectral(dir[i], channel); + KRDSP::ScaleCopy(&hrtf_impulse_sample, mix[i], hrtf_impulse, fft_size); + KRDSP::Accumulate(hrtf_accum, hrtf_impulse, fft_size); + } + } + } else { + // Low quality, non-interpolated HRTF + hrtf_spectral = getHRTFSpectral(source_direction, channel); + } + + float scale = 0.5f / fft_size; + + KRDSP::FFTForward(m_fft_setup[fft_size_log2 - KRENGINE_AUDIO_BLOCK_LOG2N], hrtf_sample, fft_size_log2); + KRDSP::Multiply(hrtf_sample, &hrtf_spectral, hrtf_convolved, fft_size); + KRDSP::FFTInverse(m_fft_setup[fft_size_log2 - KRENGINE_AUDIO_BLOCK_LOG2N], hrtf_convolved, fft_size_log2); + KRDSP::Scale(hrtf_convolved->realp, scale, fft_size); + + int output_offset = (m_output_accumulation_block_start) % (KRENGINE_REVERB_MAX_SAMPLES * KRENGINE_MAX_OUTPUT_CHANNELS); + int frames_left = fft_size; + while(frames_left) { + int frames_to_process = (KRENGINE_REVERB_MAX_SAMPLES * KRENGINE_MAX_OUTPUT_CHANNELS - output_offset) / KRENGINE_MAX_OUTPUT_CHANNELS; + if(frames_to_process > frames_left) frames_to_process = frames_left; + KRDSP::Accumulate(m_output_accumulation + output_offset + channel, KRENGINE_MAX_OUTPUT_CHANNELS, + hrtf_convolved->realp + fft_size - frames_left, 1, + frames_to_process); + frames_left -= frames_to_process; + output_offset = (output_offset + frames_to_process * KRENGINE_MAX_OUTPUT_CHANNELS) % (KRENGINE_REVERB_MAX_SAMPLES * KRENGINE_MAX_OUTPUT_CHANNELS); + } + } + } + } +} + +void KRAudioManager::renderITD() +{ + // FINDME, TODO - Need Inter-Temperal based phase shifting to support 3-d spatialized audio without headphones + + /* + + + // hrtf_kemar_H-10e000a.wav + + float speed_of_sound = 1126.0f; // feed per second FINDME - TODO - This needs to be configurable for scenes with different units + float head_radius = 0.7431f; // 0.74ft = 22cm + float half_max_itd_time = head_radius / speed_of_sound / 2.0f; // half of ITD time (Interaural time difference) when audio source is directly 90 degrees azimuth to one ear. + + // Vector3 m_listener_position; + // Vector3 m_listener_forward; + // Vector3 m_listener_up; + + Vector3 listener_right = Vector3::Cross(m_listener_forward, m_listener_up); + Vector3 listener_right_ear = m_listener_position + listener_right * head_radius / 2.0f; + Vector3 listener_left_ear = m_listener_position - listener_right * head_radius / 2.0f; + + // Get a pointer to the dataBuffer of the AudioBufferList + + Float32 *outA = (Float32 *)ioData->mBuffers[0].mData; + Float32 *outB = (Float32 *)ioData->mBuffers[1].mData; // Non-Interleaved only + + + // ----====---- Zero out accumulation / output buffer ----====---- + for (UInt32 i = 0; i < inNumberFrames; ++i) { + + // Interleaved + // outA[i*2] = (Float32)left_channel; + // outA[i*2 + 1] = (Float32)right_channel; + + // Non-Interleaved + outA[i] = (Float32)0.0f; + outB[i] = (Float32)0.0f; + } + + // ----====---- Render direct / HRTF audio ----====---- + for(std::set::iterator itr=m_activeAudioSources.begin(); itr != m_activeAudioSources.end(); itr++) { + KRAudioSource *source = *itr; + Vector3 listener_to_source = source->getWorldTranslation() - m_listener_position; + Vector3 right_ear_to_source = source->getWorldTranslation() - listener_right_ear; + Vector3 left_ear_to_source = source->getWorldTranslation() - listener_left_ear; + Vector3 source_direction = Vector3::Normalize(listener_to_source); + float right_ear_distance = right_ear_to_source.magnitude(); + float left_ear_distance = left_ear_to_source.magnitude(); + float right_itd_time = right_ear_distance / speed_of_sound; + float left_itd_time = left_ear_distance / speed_of_sound; + + float rolloff_factor = source->getRolloffFactor(); + float left_gain = 1.0f / pow(left_ear_distance / rolloff_factor, 2.0f); + float right_gain = 1.0f / pow(left_ear_distance / rolloff_factor, 2.0f); + if(left_gain > 1.0f) left_gain = 1.0f; + if(right_gain > 1.0f) right_gain = 1.0f; + left_gain *= source->getGain(); + right_gain *= source->getGain(); + int left_itd_offset = (int)(left_itd_time * 44100.0f); + int right_itd_offset = (int)(right_itd_time * 44100.0f); + KRAudioSample *sample = source->getAudioSample(); + if(sample) { + __int64_t source_start_frame = source->getStartAudioFrame(); + int sample_frame = (int)(m_audio_frame - source_start_frame); + for (UInt32 i = 0; i < inNumberFrames; ++i) { + float left_channel=sample->sample(sample_frame + left_itd_offset, 44100, 0) * left_gain; + float right_channel = sample->sample(sample_frame + right_itd_offset, 44100, 0) * right_gain; + + // left_channel = 0.0f; + // right_channel = 0.0f; + + // Interleaved + // outA[i*2] = (Float32)left_channel; + // outA[i*2 + 1] = (Float32)right_channel; + + // Non-Interleaved + outA[i] += (Float32)left_channel; + outB[i] += (Float32)right_channel; + sample_frame++; + } + } + } + + KRAudioSample *reverb_impulse_response = getContext().getAudioManager()->get("test_reverb"); + + // ----====---- Render Indirect / Reverb channel ----====---- + int buffer_frame_start=0; + int remaining_frames = inNumberFrames - buffer_frame_start; + while(remaining_frames) { + int frames_processed = remaining_frames; + if(frames_processed > KRENGINE_REVERB_FILTER_LENGTH) { + frames_processed = KRENGINE_REVERB_FILTER_LENGTH; + } + bool first_source = true; + for(std::set::iterator itr=m_activeAudioSources.begin(); itr != m_activeAudioSources.end(); itr++) { + KRAudioSource *source = *itr; + KRAudioSample *sample = source->getAudioSample(); + if(sample) { + __int64_t source_start_frame = source->getStartAudioFrame(); + int sample_frame = (int)(m_audio_frame - source_start_frame + buffer_frame_start); + + + + + for (UInt32 i = 0; i < inNumberFrames; ++i) { + if(first_source) { + sample->sample(sample_frame, 44100, 0, m_reverb_input_next_sample); + } + sample->sampleAdditive(sample_frame, 44100, 0, m_reverb_input_next_sample); + if(m_reverb_input_next_sample >= KRENGINE_REVERB_MAX_SAMPLES) { + m_reverb_input_next_sample -= KRENGINE_REVERB_MAX_SAMPLES; + } + } + } + first_source = false; + } + + + if(reverb_impulse_response) { + // Perform FFT Convolution for Impulse-response based reverb + + memcpy(m_reverb_accumulation, m_reverb_accumulation + KRENGINE_REVERB_FILTER_LENGTH, KRENGINE_REVERB_FILTER_LENGTH * sizeof(float)); + memset(m_reverb_accumulation + KRENGINE_REVERB_FILTER_LENGTH, 0, KRENGINE_REVERB_FILTER_LENGTH * sizeof(float)); + } + + buffer_frame_start += frames_processed; + remaining_frames = inNumberFrames - buffer_frame_start; + } + + + */ +} + +static bool audioIsMuted = false; +static bool audioShouldBecomeMuted = false; +static bool audioShouldBecomeUnmuted = false; + +void audioLimit_Mute(bool onNotOff) { + if (onNotOff) { + if (audioIsMuted) { + audioShouldBecomeMuted = false; + audioShouldBecomeUnmuted = false; + return; + } + audioShouldBecomeMuted = true; + audioShouldBecomeUnmuted = false; + } + else { + if (!audioIsMuted) { + audioShouldBecomeMuted = false; + audioShouldBecomeUnmuted = false; + return; + } + audioShouldBecomeMuted = false; + audioShouldBecomeUnmuted = true; + } +} + +float audioGetLimitParameters_Stereo(float *buffer, unsigned long framesize, + unsigned long *attack_sample_position, float *peak) +{ + float limitvol = 1.0; + long attack_position = -1; + *peak = 0.0; + float max = 0.0; + float amplitude = 0.0; + + float *src = buffer; + for (unsigned long i = 0; i < framesize * 2; i++) { + amplitude = fabs(*src); src++; + if (amplitude > max) max = amplitude; + if (amplitude > 0.995) if (attack_position < 0) attack_position = (i+1) / 2; + } + if (max > 0.995) limitvol = 0.995 / max; + *peak = max; + + if (attack_position < 0) attack_position = framesize; + *attack_sample_position = (unsigned long) attack_position; + return limitvol; +} // returns the new limit volume, *attack_sample_position tells how fast we need to reach the new limit + +void audioLimit_Stereo(float *stereo_buffer, unsigned long framesize) +{ + static float limit_value = 1.0; + + // (1) get the limiting parameters for the incoming audio data + float previouslimitvol = limit_value; + float peak; + unsigned long attack_sample_position = framesize; + + // (1a) check for a mute or unmute state then get the next limit volume + float nextlimitvol = 0.0; + if (audioIsMuted && audioShouldBecomeUnmuted) { audioIsMuted = false; audioShouldBecomeUnmuted = false; } + if (audioShouldBecomeMuted) { audioIsMuted = true; audioShouldBecomeMuted = false; } + if (!audioIsMuted) nextlimitvol = audioGetLimitParameters_Stereo(stereo_buffer, framesize, &attack_sample_position, &peak); + + // (1b) if no limiting is needed then return + if ((1.0 == nextlimitvol) && (1.0 == previouslimitvol)) { return; } // no limiting necessary + + // (2) calculate limiting factors + float deltavol = 0.0; + if (previouslimitvol != nextlimitvol) { + deltavol = (nextlimitvol - previouslimitvol) / (float) attack_sample_position; + } + + // (3) do the limiting + float *src = stereo_buffer; + + if (0.0 == deltavol) { // fixed volume + for (unsigned long i=0; i < framesize; i++) { + *src = *src * nextlimitvol; + src++; + *src = *src * nextlimitvol; + src++; + } + } + else { + for (unsigned long i=0; i < attack_sample_position; i++) { // attack phase + *src = *src * previouslimitvol; + src++; + *src = *src * previouslimitvol; + src++; + previouslimitvol += deltavol; + } + if (nextlimitvol < 1.0) { // plateau phase + for (unsigned long i = attack_sample_position; i < framesize; i++) { + *src = *src * nextlimitvol; + src++; + *src = *src * nextlimitvol; + src++; + } + } + } + + // (4) save our limit level for next time + limit_value = nextlimitvol; +} + +void KRAudioManager::mute(bool onNotOff) +{ + audioLimit_Mute(onNotOff); +} + +void KRAudioManager::renderLimiter() +{ + int output_offset = (m_output_accumulation_block_start) % (KRENGINE_REVERB_MAX_SAMPLES * KRENGINE_MAX_OUTPUT_CHANNELS); + float *output = m_output_accumulation + output_offset; + unsigned long numframes = KRENGINE_AUDIO_BLOCK_LENGTH; + audioLimit_Stereo(output, numframes); +} + +void KRAudioManager::goToSleep() +{ + cleanupAudio(); +} diff --git a/kraken/KRBone.cpp b/kraken/KRBone.cpp index a9619b1..a64f497 100755 --- a/kraken/KRBone.cpp +++ b/kraken/KRBone.cpp @@ -1,97 +1,97 @@ -// -// KRBone.cpp -// KREngine -// -// Created by Kearwood Gilbert on 2012-12-06. -// Copyright (c) 2012 Kearwood Software. All rights reserved. -// - -#include "KRBone.h" -#include "KRContext.h" - -KRBone::KRBone(KRScene &scene, std::string name) : KRNode(scene, name) -{ - setScaleCompensation(true); -} - -KRBone::~KRBone() -{ -} - -std::string KRBone::getElementName() { - return "bone"; -} - -tinyxml2::XMLElement *KRBone::saveXML( tinyxml2::XMLNode *parent) -{ - tinyxml2::XMLElement *e = KRNode::saveXML(parent); - - return e; -} - -void KRBone::loadXML(tinyxml2::XMLElement *e) -{ - KRNode::loadXML(e); - setScaleCompensation(true); -} - -AABB KRBone::getBounds() { - return AABB(-Vector3::One(), Vector3::One(), getModelMatrix()); // Only required for bone debug visualization -} - -void KRBone::render(KRCamera *pCamera, std::vector &point_lights, std::vector &directional_lights, std::vector&spot_lights, const KRViewport &viewport, KRNode::RenderPass renderPass) -{ - if(m_lod_visible <= LOD_VISIBILITY_PRESTREAM) return; - - KRNode::render(pCamera, point_lights, directional_lights, spot_lights, viewport, renderPass); - - bool bVisualize = pCamera->settings.debug_display == KRRenderSettings::KRENGINE_DEBUG_DISPLAY_BONES; - - if(renderPass == KRNode::RENDER_PASS_FORWARD_TRANSPARENT && bVisualize) { - Matrix4 sphereModelMatrix = getModelMatrix(); - - // Enable additive blending - GLDEBUG(glEnable(GL_BLEND)); - GLDEBUG(glBlendFunc(GL_ONE, GL_ONE)); - - - // Disable z-buffer write - GLDEBUG(glDepthMask(GL_FALSE)); - - // Disable z-buffer test - GLDEBUG(glDisable(GL_DEPTH_TEST)); - - KRShader *pShader = getContext().getShaderManager()->getShader("visualize_overlay", pCamera, point_lights, directional_lights, spot_lights, 0, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, renderPass); - - if(getContext().getShaderManager()->selectShader(*pCamera, pShader, viewport, sphereModelMatrix, point_lights, directional_lights, spot_lights, 0, renderPass, Vector3::Zero(), 0.0f, Vector4::Zero())) { - std::vector sphereModels = getContext().getMeshManager()->getModel("__sphere"); - if(sphereModels.size()) { - for(int i=0; i < sphereModels[0]->getSubmeshCount(); i++) { - sphereModels[0]->renderSubmesh(i, renderPass, getName(), "visualize_overlay", 1.0f); - } - } - - } - - // Enable alpha blending - GLDEBUG(glEnable(GL_BLEND)); - GLDEBUG(glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA)); - - // Enable z-buffer test - GLDEBUG(glEnable(GL_DEPTH_TEST)); - GLDEBUG(glDepthFunc(GL_LEQUAL)); - GLDEBUG(glDepthRangef(0.0, 1.0)); - - - } -} - - -void KRBone::setBindPose(const Matrix4 &pose) -{ - m_bind_pose = pose; -} -const Matrix4 &KRBone::getBindPose() -{ - return m_bind_pose; -} +// +// KRBone.cpp +// KREngine +// +// Created by Kearwood Gilbert on 2012-12-06. +// Copyright (c) 2012 Kearwood Software. All rights reserved. +// + +#include "KRBone.h" +#include "KRContext.h" + +KRBone::KRBone(KRScene &scene, std::string name) : KRNode(scene, name) +{ + setScaleCompensation(true); +} + +KRBone::~KRBone() +{ +} + +std::string KRBone::getElementName() { + return "bone"; +} + +tinyxml2::XMLElement *KRBone::saveXML( tinyxml2::XMLNode *parent) +{ + tinyxml2::XMLElement *e = KRNode::saveXML(parent); + + return e; +} + +void KRBone::loadXML(tinyxml2::XMLElement *e) +{ + KRNode::loadXML(e); + setScaleCompensation(true); +} + +AABB KRBone::getBounds() { + return AABB::Create(-Vector3::One(), Vector3::One(), getModelMatrix()); // Only required for bone debug visualization +} + +void KRBone::render(KRCamera *pCamera, std::vector &point_lights, std::vector &directional_lights, std::vector&spot_lights, const KRViewport &viewport, KRNode::RenderPass renderPass) +{ + if(m_lod_visible <= LOD_VISIBILITY_PRESTREAM) return; + + KRNode::render(pCamera, point_lights, directional_lights, spot_lights, viewport, renderPass); + + bool bVisualize = pCamera->settings.debug_display == KRRenderSettings::KRENGINE_DEBUG_DISPLAY_BONES; + + if(renderPass == KRNode::RENDER_PASS_FORWARD_TRANSPARENT && bVisualize) { + Matrix4 sphereModelMatrix = getModelMatrix(); + + // Enable additive blending + GLDEBUG(glEnable(GL_BLEND)); + GLDEBUG(glBlendFunc(GL_ONE, GL_ONE)); + + + // Disable z-buffer write + GLDEBUG(glDepthMask(GL_FALSE)); + + // Disable z-buffer test + GLDEBUG(glDisable(GL_DEPTH_TEST)); + + KRShader *pShader = getContext().getShaderManager()->getShader("visualize_overlay", pCamera, point_lights, directional_lights, spot_lights, 0, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, renderPass); + + if(getContext().getShaderManager()->selectShader(*pCamera, pShader, viewport, sphereModelMatrix, point_lights, directional_lights, spot_lights, 0, renderPass, Vector3::Zero(), 0.0f, Vector4::Zero())) { + std::vector sphereModels = getContext().getMeshManager()->getModel("__sphere"); + if(sphereModels.size()) { + for(int i=0; i < sphereModels[0]->getSubmeshCount(); i++) { + sphereModels[0]->renderSubmesh(i, renderPass, getName(), "visualize_overlay", 1.0f); + } + } + + } + + // Enable alpha blending + GLDEBUG(glEnable(GL_BLEND)); + GLDEBUG(glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA)); + + // Enable z-buffer test + GLDEBUG(glEnable(GL_DEPTH_TEST)); + GLDEBUG(glDepthFunc(GL_LEQUAL)); + GLDEBUG(glDepthRangef(0.0, 1.0)); + + + } +} + + +void KRBone::setBindPose(const Matrix4 &pose) +{ + m_bind_pose = pose; +} +const Matrix4 &KRBone::getBindPose() +{ + return m_bind_pose; +} diff --git a/kraken/KRCamera.cpp b/kraken/KRCamera.cpp index 551d28d..ecdfb07 100755 --- a/kraken/KRCamera.cpp +++ b/kraken/KRCamera.cpp @@ -1,1123 +1,1123 @@ -// -// KRCamera.cpp -// KREngine -// -// Copyright 2012 Kearwood Gilbert. All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, are -// permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of -// conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list -// of conditions and the following disclaimer in the documentation and/or other materials -// provided with the distribution. -// -// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED -// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR -// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// The views and conclusions contained in the software and documentation are those of the -// authors and should not be interpreted as representing official policies, either expressed -// or implied, of Kearwood Gilbert. -// - -#include "KREngine-common.h" -#include "KRCamera.h" -#include "KRStockGeometry.h" -#include "KRDirectionalLight.h" - -KRCamera::KRCamera(KRScene &scene, std::string name) : KRNode(scene, name) { - m_last_frame_start = 0; - - m_particlesAbsoluteTime = 0.0f; - m_backingWidth = 0; - m_backingHeight = 0; - volumetricBufferWidth = 0; - volumetricBufferHeight = 0; - m_pSkyBoxTexture = NULL; - - compositeDepthTexture = 0; - compositeColorTexture = 0; - lightAccumulationTexture = 0; - compositeFramebuffer = 0; - lightAccumulationBuffer = 0; - - volumetricLightAccumulationBuffer = 0; - volumetricLightAccumulationTexture = 0; - m_frame_times_filled = 0; - m_downsample = Vector2::One(); - - m_fade_color = Vector4::Zero(); -} - -KRCamera::~KRCamera() { - destroyBuffers(); -} - -std::string KRCamera::getElementName() { - return "camera"; -} - -tinyxml2::XMLElement *KRCamera::saveXML( tinyxml2::XMLNode *parent) -{ - tinyxml2::XMLElement *e = KRNode::saveXML(parent); - e->SetAttribute("skybox", m_skyBox.c_str()); - - return e; -} - - -void KRCamera::loadXML(tinyxml2::XMLElement *e) -{ - KRNode::loadXML(e); - const char *szSkyBoxName = e->Attribute("skybox"); - m_skyBox = szSkyBoxName ? szSkyBoxName : ""; -} - -void KRCamera::setSkyBox(const std::string &skyBox) -{ - m_pSkyBoxTexture = NULL; - m_skyBox = skyBox; - -} - -const std::string KRCamera::getSkyBox() const -{ - return m_skyBox; -} - -void KRCamera::renderFrame(GLint defaultFBO, GLint renderBufferWidth, GLint renderBufferHeight) -{ - // ----====---- Record timing information for measuring FPS ----====---- - uint64_t current_time = m_pContext->getAbsoluteTimeMilliseconds(); - if(m_last_frame_start != 0) { - m_frame_times[m_pContext->getCurrentFrame() % KRAKEN_FPS_AVERAGE_FRAME_COUNT] = (int)(current_time - m_last_frame_start); - if(m_frame_times_filled < KRAKEN_FPS_AVERAGE_FRAME_COUNT) m_frame_times_filled++; - } - m_last_frame_start = current_time; - - createBuffers(renderBufferWidth, renderBufferHeight); - - KRScene &scene = getScene(); - - Matrix4 modelMatrix = getModelMatrix(); - Matrix4 viewMatrix = Matrix4::LookAt(Matrix4::Dot(modelMatrix, Vector3::Zero()), Matrix4::Dot(modelMatrix, Vector3::Forward()), Vector3::Normalize(Matrix4::DotNoTranslate(modelMatrix, Vector3::Up()))); - - //Matrix4 viewMatrix = Matrix4::Invert(getModelMatrix()); - - settings.setViewportSize(Vector2(m_backingWidth, m_backingHeight)); - Matrix4 projectionMatrix; - projectionMatrix.perspective(settings.perspective_fov, settings.m_viewportSize.x / settings.m_viewportSize.y, settings.perspective_nearz, settings.perspective_farz); - m_viewport = KRViewport(settings.getViewportSize(), viewMatrix, projectionMatrix); - m_viewport.setLODBias(settings.getLODBias()); - - Vector3 vecCameraDirection = m_viewport.getCameraDirection(); - - - scene.updateOctree(m_viewport); - - // ----====---- Pre-stream resources ----====---- - scene.render(this, m_viewport.getVisibleBounds(), m_viewport, KRNode::RENDER_PASS_PRESTREAM, true); - - // ----====---- Generate Shadowmaps for Lights ----====---- - if(settings.m_cShadowBuffers > 0) { - GL_PUSH_GROUP_MARKER("Generate Shadowmaps"); - - scene.render(this, m_viewport.getVisibleBounds(), m_viewport, KRNode::RENDER_PASS_GENERATE_SHADOWMAPS, false /*settings.bEnableDeferredLighting*/); - GLDEBUG(glViewport(0, 0, m_viewport.getSize().x, m_viewport.getSize().y)); - GL_POP_GROUP_MARKER; - } - - if(settings.bEnableDeferredLighting) { - - // ----====---- Opaque Geometry, Deferred rendering Pass 1 ----====---- - - GL_PUSH_GROUP_MARKER("Deferred Lighting - Pass 1 (Opaque)"); - - // Set render target - GLDEBUG(glBindFramebuffer(GL_FRAMEBUFFER, compositeFramebuffer)); - - -#if GL_EXT_discard_framebuffer - GLenum attachments[2] = {GL_DEPTH_ATTACHMENT, GL_COLOR_ATTACHMENT0}; - GLDEBUG(glDiscardFramebufferEXT(GL_FRAMEBUFFER, 2, attachments)); -#endif - - // Enable z-buffer write - GLDEBUG(glDepthMask(GL_TRUE)); - - GLDEBUG(glClearColor(0.0f, 0.0f, 0.0f, 0.0f)); - GLDEBUG(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)); - - // Enable backface culling - GLDEBUG(glCullFace(GL_BACK)); - GLDEBUG(glEnable(GL_CULL_FACE)); - - // Enable z-buffer test - GLDEBUG(glEnable(GL_DEPTH_TEST)); - GLDEBUG(glDepthFunc(GL_LEQUAL)); - GLDEBUG(glDepthRangef(0.0, 1.0)); - - // Disable alpha blending - GLDEBUG(glDisable(GL_BLEND)); - - // Render the geometry - scene.render(this, m_viewport.getVisibleBounds(), m_viewport, KRNode::RENDER_PASS_DEFERRED_GBUFFER, false); - - - GL_POP_GROUP_MARKER; - - - - // ----====---- Opaque Geometry, Deferred rendering Pass 2 ----====---- - - GL_PUSH_GROUP_MARKER("Deferred Lighting - Pass 2 (Opaque)"); - - // Set render target - GLDEBUG(glBindFramebuffer(GL_FRAMEBUFFER, lightAccumulationBuffer)); - GLDEBUG(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, compositeDepthTexture, 0)); - GLDEBUG(glViewport(0, 0, m_viewport.getSize().x * m_downsample.x, m_viewport.getSize().y * m_downsample.y)); - GLDEBUG(glClearColor(0.0f, 0.0f, 0.0f, 0.0f)); - GLDEBUG(glClear(GL_COLOR_BUFFER_BIT)); - - // Enable additive blending - GLDEBUG(glEnable(GL_BLEND)); - GLDEBUG(glBlendFunc(GL_ONE, GL_ONE)); - - // Disable z-buffer write - GLDEBUG(glDepthMask(GL_FALSE)); - - // Set source to buffers from pass 1 - m_pContext->getTextureManager()->selectTexture(GL_TEXTURE_2D, 6, compositeColorTexture); - m_pContext->getTextureManager()->selectTexture(GL_TEXTURE_2D, 7, compositeDepthTexture); - - - // Render the geometry - scene.render(this, m_viewport.getVisibleBounds(), m_viewport, KRNode::RENDER_PASS_DEFERRED_LIGHTS, false); - - GL_POP_GROUP_MARKER; - - // ----====---- Opaque Geometry, Deferred rendering Pass 3 ----====---- - - GL_PUSH_GROUP_MARKER("Deferred Lighting - Pass 3 (Opaque)"); - - // Set render target - GLDEBUG(glBindFramebuffer(GL_FRAMEBUFFER, compositeFramebuffer)); - GLDEBUG(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, compositeDepthTexture, 0)); - - // Disable alpha blending - GLDEBUG(glDisable(GL_BLEND)); - - GLDEBUG(glClearColor(0.0f, 0.0f, 0.0f, 1.0f)); - GLDEBUG(glClear(GL_COLOR_BUFFER_BIT)); - - // Set source to buffers from pass 2 - m_pContext->getTextureManager()->selectTexture(GL_TEXTURE_2D, 6, lightAccumulationTexture); - - // Enable backface culling - GLDEBUG(glCullFace(GL_BACK)); - GLDEBUG(glEnable(GL_CULL_FACE)); - - // Enable z-buffer test - GLDEBUG(glEnable(GL_DEPTH_TEST)); - GLDEBUG(glDepthFunc(GL_LEQUAL)); - GLDEBUG(glDepthRangef(0.0, 1.0)); - - // Enable z-buffer write - GLDEBUG(glDepthMask(GL_TRUE)); - - // Render the geometry - // TODO: At this point, we only want to render octree nodes that produced fragments during the 1st pass into the GBuffer - scene.render(this, m_viewport.getVisibleBounds(), m_viewport, KRNode::RENDER_PASS_DEFERRED_OPAQUE, false); - - // Deactivate source buffer texture units - m_pContext->getTextureManager()->selectTexture(GL_TEXTURE_2D, 6, 0); - m_pContext->getTextureManager()->selectTexture(GL_TEXTURE_2D, 7, 0); - - GL_POP_GROUP_MARKER; - } else { - // ----====---- Opaque Geometry, Forward Rendering ----====---- - - GL_PUSH_GROUP_MARKER("Forward Rendering - Opaque"); - - // Set render target - GLDEBUG(glBindFramebuffer(GL_FRAMEBUFFER, compositeFramebuffer)); - GLDEBUG(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, compositeDepthTexture, 0)); - GLDEBUG(glViewport(0, 0, m_viewport.getSize().x * m_downsample.x, m_viewport.getSize().y * m_downsample.y)); - - // Disable alpha blending - GLDEBUG(glDisable(GL_BLEND)); - - GLDEBUG(glClearColor(0.0f, 0.0f, 0.0f, 1.0f)); - - // Enable z-buffer write - GLDEBUG(glDepthMask(GL_TRUE)); - - -#if GL_EXT_discard_framebuffer - GLenum attachments[2] = {GL_DEPTH_ATTACHMENT, GL_COLOR_ATTACHMENT0}; - GLDEBUG(glDiscardFramebufferEXT(GL_FRAMEBUFFER, 2, attachments)); -#endif - GLDEBUG(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)); - - // Enable backface culling - GLDEBUG(glCullFace(GL_BACK)); - GLDEBUG(glEnable(GL_CULL_FACE)); - - - - // Enable z-buffer test - GLDEBUG(glEnable(GL_DEPTH_TEST)); - GLDEBUG(glDepthFunc(GL_LEQUAL)); - GLDEBUG(glDepthRangef(0.0, 1.0)); - - - - // Render the geometry - scene.render(this, m_viewport.getVisibleBounds(), m_viewport, KRNode::RENDER_PASS_FORWARD_OPAQUE, false); - - GL_POP_GROUP_MARKER; - } - - // ----====---- Sky Box ----====---- - - - GL_PUSH_GROUP_MARKER("Sky Box"); - - // Set render target - GLDEBUG(glBindFramebuffer(GL_FRAMEBUFFER, compositeFramebuffer)); - GLDEBUG(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, compositeDepthTexture, 0)); - - // Disable backface culling - GLDEBUG(glDisable(GL_CULL_FACE)); - - // Disable z-buffer write - GLDEBUG(glDepthMask(GL_FALSE)); - - // Enable z-buffer test - GLDEBUG(glEnable(GL_DEPTH_TEST)); - GLDEBUG(glDepthFunc(GL_LEQUAL)); - GLDEBUG(glDepthRangef(0.0, 1.0)); - - if(!m_pSkyBoxTexture && m_skyBox.length()) { - m_pSkyBoxTexture = getContext().getTextureManager()->getTextureCube(m_skyBox.c_str()); - } - - if(m_pSkyBoxTexture) { - getContext().getShaderManager()->selectShader("sky_box", *this, std::vector(), std::vector(), std::vector(), 0, m_viewport, Matrix4(), false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, KRNode::RENDER_PASS_FORWARD_OPAQUE, Vector3::Zero(), 0.0f, Vector4::Zero()); - - getContext().getTextureManager()->selectTexture(0, m_pSkyBoxTexture, 0.0f, KRTexture::TEXTURE_USAGE_SKY_CUBE); - - // Render a full screen quad - m_pContext->getMeshManager()->bindVBO(&getContext().getMeshManager()->KRENGINE_VBO_DATA_2D_SQUARE_VERTICES, 1.0f); - GLDEBUG(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)); - } - - GL_POP_GROUP_MARKER; - - - // ----====---- Transparent Geometry, Forward Rendering ----====---- - - GL_PUSH_GROUP_MARKER("Forward Rendering - Transparent"); - -// Note: These parameters have already been set up by the skybox render above -// -// // Set render target -// GLDEBUG(glBindFramebuffer(GL_FRAMEBUFFER, compositeFramebuffer)); -// GLDEBUG(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, compositeDepthTexture, 0)); -// -// // Disable backface culling -// GLDEBUG(glDisable(GL_CULL_FACE)); -// - // Enable backface culling - GLDEBUG(glCullFace(GL_BACK)); - GLDEBUG(glEnable(GL_CULL_FACE)); - - // Disable z-buffer write - GLDEBUG(glDepthMask(GL_FALSE)); -// -// // Enable z-buffer test -// GLDEBUG(glEnable(GL_DEPTH_TEST)); -// GLDEBUG(glDepthFunc(GL_LEQUAL)); -// GLDEBUG(glDepthRangef(0.0, 1.0)); - - // Enable alpha blending - GLDEBUG(glEnable(GL_BLEND)); - GLDEBUG(glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA)); - - // Render all transparent geometry - scene.render(this, m_viewport.getVisibleBounds(), m_viewport, KRNode::RENDER_PASS_FORWARD_TRANSPARENT, false); - - GL_POP_GROUP_MARKER; - - // ----====---- Particle Occlusion Tests ----====---- - - GL_PUSH_GROUP_MARKER("Particle Occlusion Tests"); - - // Set render target - GLDEBUG(glBindFramebuffer(GL_FRAMEBUFFER, compositeFramebuffer)); - GLDEBUG(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, compositeDepthTexture, 0)); - - // Disable backface culling - GLDEBUG(glDisable(GL_CULL_FACE)); - - // Disable z-buffer write - GLDEBUG(glDepthMask(GL_FALSE)); - - // Enable z-buffer test - GLDEBUG(glEnable(GL_DEPTH_TEST)); - GLDEBUG(glDepthFunc(GL_LEQUAL)); - GLDEBUG(glDepthRangef(0.0, 1.0)); - - // Enable additive blending - GLDEBUG(glEnable(GL_BLEND)); - GLDEBUG(glBlendFunc(GL_ONE, GL_ONE)); - - // ----====---- Perform Occlusion Tests ----====---- - scene.render(this, m_viewport.getVisibleBounds(), m_viewport, RENDER_PASS_PARTICLE_OCCLUSION, false); - - GL_POP_GROUP_MARKER; - - // ----====---- Flares ----====---- - - GL_PUSH_GROUP_MARKER("Additive Particles"); - - // Set render target - GLDEBUG(glBindFramebuffer(GL_FRAMEBUFFER, compositeFramebuffer)); - GLDEBUG(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, compositeDepthTexture, 0)); - - // Disable backface culling - GLDEBUG(glDisable(GL_CULL_FACE)); - - // Disable z-buffer write - GLDEBUG(glDepthMask(GL_FALSE)); - - // Disable z-buffer test - GLDEBUG(glDisable(GL_DEPTH_TEST)); - GLDEBUG(glDepthRangef(0.0, 1.0)); - - // Enable additive blending - GLDEBUG(glEnable(GL_BLEND)); - GLDEBUG(glBlendFunc(GL_ONE, GL_ONE)); - - // Render all flares - scene.render(this, m_viewport.getVisibleBounds(), m_viewport, KRNode::RENDER_PASS_ADDITIVE_PARTICLES, false); - - GL_POP_GROUP_MARKER; - - // ----====---- Volumetric Lighting ----====---- - - if(settings.volumetric_environment_enable) { - - GL_PUSH_GROUP_MARKER("Volumetric Lighting"); - - KRViewport volumetricLightingViewport = KRViewport(Vector2(volumetricBufferWidth, volumetricBufferHeight), m_viewport.getViewMatrix(), m_viewport.getProjectionMatrix()); - - if(settings.volumetric_environment_downsample != 0) { - // Set render target - GLDEBUG(glBindFramebuffer(GL_FRAMEBUFFER, volumetricLightAccumulationBuffer)); - GLDEBUG(glClearColor(0.0f, 0.0f, 0.0f, 0.0f)); - GLDEBUG(glClear(GL_COLOR_BUFFER_BIT)); - - // Disable z-buffer test - GLDEBUG(glDisable(GL_DEPTH_TEST)); - m_pContext->getTextureManager()->selectTexture(GL_TEXTURE_2D, 0, compositeDepthTexture); - - GLDEBUG(glViewport(0, 0, volumetricLightingViewport.getSize().x, volumetricLightingViewport.getSize().y)); - } else { - // Enable z-buffer test - GLDEBUG(glEnable(GL_DEPTH_TEST)); - GLDEBUG(glDepthFunc(GL_LEQUAL)); - GLDEBUG(glDepthRangef(0.0, 1.0)); - } - - scene.render(this, m_viewport.getVisibleBounds(), volumetricLightingViewport, KRNode::RENDER_PASS_VOLUMETRIC_EFFECTS_ADDITIVE, false); - - if(settings.volumetric_environment_downsample != 0) { - // Set render target - GLDEBUG(glBindFramebuffer(GL_FRAMEBUFFER, compositeFramebuffer)); - GLDEBUG(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, compositeDepthTexture, 0)); - - GLDEBUG(glViewport(0, 0, m_viewport.getSize().x, m_viewport.getSize().y)); - } - - GL_POP_GROUP_MARKER; - } - - - - // ----====---- Debug Overlay ----====---- - - GL_PUSH_GROUP_MARKER("Debug Overlays"); - - if(settings.debug_display == KRRenderSettings::KRENGINE_DEBUG_DISPLAY_OCTREE) { - // Enable z-buffer test - GLDEBUG(glEnable(GL_DEPTH_TEST)); - GLDEBUG(glDepthRangef(0.0, 1.0)); - - - // Enable backface culling - GLDEBUG(glCullFace(GL_BACK)); - GLDEBUG(glEnable(GL_CULL_FACE)); - - // Enable additive blending - GLDEBUG(glEnable(GL_BLEND)); - GLDEBUG(glBlendFunc(GL_ONE, GL_ONE)); - - - KRShader *pVisShader = getContext().getShaderManager()->getShader("visualize_overlay", this, std::vector(), std::vector(), std::vector(), 0, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, KRNode::RENDER_PASS_FORWARD_TRANSPARENT); - - m_pContext->getMeshManager()->bindVBO(&getContext().getMeshManager()->KRENGINE_VBO_DATA_3D_CUBE_VERTICES, 1.0f); - for(unordered_map::iterator itr=m_viewport.getVisibleBounds().begin(); itr != m_viewport.getVisibleBounds().end(); itr++) { - Matrix4 matModel = Matrix4(); - matModel.scale((*itr).first.size() * 0.5f); - matModel.translate((*itr).first.center()); - if(getContext().getShaderManager()->selectShader(*this, pVisShader, m_viewport, matModel, std::vector(), std::vector(), std::vector(), 0, KRNode::RENDER_PASS_FORWARD_TRANSPARENT, Vector3::Zero(), 0.0f, Vector4::Zero())) { - GLDEBUG(glDrawArrays(GL_TRIANGLE_STRIP, 0, 14)); - } - } - } - - // Re-enable z-buffer write - GLDEBUG(glDepthMask(GL_TRUE)); - - GL_POP_GROUP_MARKER; - - -// fprintf(stderr, "VBO Mem: %i Kbyte Texture Mem: %i/%i Kbyte (active/total) Shader Handles: %i Visible Bounds: %i Max Texture LOD: %i\n", (int)m_pContext->getMeshManager()->getMemUsed() / 1024, (int)m_pContext->getTextureManager()->getActiveMemUsed() / 1024, (int)m_pContext->getTextureManager()->getMemUsed() / 1024, (int)m_pContext->getShaderManager()->getShaderHandlesUsed(), (int)m_visibleBounds.size(), m_pContext->getTextureManager()->getLODDimCap()); - GL_PUSH_GROUP_MARKER("Post Processing"); - - GLDEBUG(glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO)); - renderPost(); - m_pContext->getMeshManager()->unbindVBO(); - - GL_POP_GROUP_MARKER; - - -#if GL_EXT_discard_framebuffer - - GLenum attachments[2] = {GL_DEPTH_ATTACHMENT, GL_COLOR_ATTACHMENT0}; - GLDEBUG(glDiscardFramebufferEXT(GL_FRAMEBUFFER, 2, attachments)); -#endif -} - - -void KRCamera::createBuffers(GLint renderBufferWidth, GLint renderBufferHeight) { - - if(renderBufferWidth != m_backingWidth || renderBufferHeight != m_backingHeight) { - m_backingWidth = renderBufferWidth; - m_backingHeight = renderBufferHeight; - - if (compositeDepthTexture) { - GLDEBUG(glDeleteTextures(1, &compositeDepthTexture)); - compositeDepthTexture = 0; - } - - if (compositeColorTexture) { - GLDEBUG(glDeleteTextures(1, &compositeColorTexture)); - compositeColorTexture = 0; - } - - if (lightAccumulationTexture) { - GLDEBUG(glDeleteTextures(1, &lightAccumulationTexture)); - lightAccumulationTexture = 0; - } - - if (compositeFramebuffer) { - GLDEBUG(glDeleteFramebuffers(1, &compositeFramebuffer)); - compositeFramebuffer = 0; - } - - if (lightAccumulationBuffer) { - GLDEBUG(glDeleteFramebuffers(1, &lightAccumulationBuffer)); - lightAccumulationBuffer = 0; - } - - - // ===== Create offscreen compositing framebuffer object ===== - GLDEBUG(glGenFramebuffers(1, &compositeFramebuffer)); - GLDEBUG(glBindFramebuffer(GL_FRAMEBUFFER, compositeFramebuffer)); - - // ----- Create texture color buffer for compositeFramebuffer ----- - GLDEBUG(glGenTextures(1, &compositeColorTexture)); - GLDEBUG(glBindTexture(GL_TEXTURE_2D, compositeColorTexture)); - GLDEBUG(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); - GLDEBUG(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); - GLDEBUG(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); // This is necessary for non-power-of-two textures - GLDEBUG(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); // This is necessary for non-power-of-two textures - GLDEBUG(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_backingWidth, m_backingHeight, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL)); - GLDEBUG(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, compositeColorTexture, 0)); - - // ----- Create Depth Texture for compositeFramebuffer ----- - GLDEBUG(glGenTextures(1, &compositeDepthTexture)); - GLDEBUG(glBindTexture(GL_TEXTURE_2D, compositeDepthTexture)); - GLDEBUG(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)); - GLDEBUG(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)); - GLDEBUG(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); // This is necessary for non-power-of-two textures - GLDEBUG(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); // This is necessary for non-power-of-two textures - GLDEBUG(glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, m_backingWidth, m_backingHeight, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL)); - //GLDEBUG(glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT16, m_backingWidth, m_backingHeight, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL)); - //GLDEBUG(glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24_OES, m_backingWidth, m_backingHeight)); - GLDEBUG(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, compositeDepthTexture, 0)); - - // ===== Create offscreen compositing framebuffer object ===== - GLDEBUG(glGenFramebuffers(1, &lightAccumulationBuffer)); - GLDEBUG(glBindFramebuffer(GL_FRAMEBUFFER, lightAccumulationBuffer)); - - // ----- Create texture color buffer for compositeFramebuffer ----- - GLDEBUG(glGenTextures(1, &lightAccumulationTexture)); - GLDEBUG(glBindTexture(GL_TEXTURE_2D, lightAccumulationTexture)); - GLDEBUG(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); - GLDEBUG(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); - GLDEBUG(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); // This is necessary for non-power-of-two textures - GLDEBUG(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); // This is necessary for non-power-of-two textures - GLDEBUG(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_backingWidth, m_backingHeight, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL)); - GLDEBUG(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, lightAccumulationTexture, 0)); - } - - int targetVolumetricBufferWidth = 0; - int targetVolumetricBufferHeight = 0; - if(settings.volumetric_environment_enable && settings.volumetric_environment_downsample != 0) { - targetVolumetricBufferWidth = renderBufferWidth >> settings.volumetric_environment_downsample; - targetVolumetricBufferHeight = renderBufferHeight >> settings.volumetric_environment_downsample; - } - - - if(targetVolumetricBufferWidth != volumetricBufferWidth || targetVolumetricBufferHeight != volumetricBufferHeight) { - volumetricBufferWidth = targetVolumetricBufferWidth; - volumetricBufferHeight = targetVolumetricBufferHeight; - - if (volumetricLightAccumulationTexture) { - GLDEBUG(glDeleteTextures(1, &volumetricLightAccumulationTexture)); - volumetricLightAccumulationTexture = 0; - } - - if (volumetricLightAccumulationBuffer) { - GLDEBUG(glDeleteFramebuffers(1, &volumetricLightAccumulationBuffer)); - volumetricLightAccumulationBuffer = 0; - } - - - if(targetVolumetricBufferWidth != 0 && targetVolumetricBufferHeight != 0) { - // ===== Create offscreen compositing framebuffer object for volumetric lighting ===== - GLDEBUG(glGenFramebuffers(1, &volumetricLightAccumulationBuffer)); - GLDEBUG(glBindFramebuffer(GL_FRAMEBUFFER, volumetricLightAccumulationBuffer)); - - // ----- Create texture color buffer for compositeFramebuffer for volumetric lighting ----- - GLDEBUG(glGenTextures(1, &volumetricLightAccumulationTexture)); - GLDEBUG(glBindTexture(GL_TEXTURE_2D, volumetricLightAccumulationTexture)); - GLDEBUG(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); - GLDEBUG(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); - GLDEBUG(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); // This is necessary for non-power-of-two textures - GLDEBUG(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); // This is necessary for non-power-of-two textures - GLDEBUG(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, volumetricBufferWidth, volumetricBufferHeight, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL)); - GLDEBUG(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, volumetricLightAccumulationTexture, 0)); - } - } -} - -void KRCamera::destroyBuffers() -{ - if (compositeDepthTexture) { - GLDEBUG(glDeleteTextures(1, &compositeDepthTexture)); - compositeDepthTexture = 0; - } - - if (compositeColorTexture) { - GLDEBUG(glDeleteTextures(1, &compositeColorTexture)); - compositeColorTexture = 0; - } - - if (lightAccumulationTexture) { - GLDEBUG(glDeleteTextures(1, &lightAccumulationTexture)); - lightAccumulationTexture = 0; - } - - if (compositeFramebuffer) { - GLDEBUG(glDeleteFramebuffers(1, &compositeFramebuffer)); - compositeFramebuffer = 0; - } - - if (lightAccumulationBuffer) { - GLDEBUG(glDeleteFramebuffers(1, &lightAccumulationBuffer)); - lightAccumulationBuffer = 0; - } - - if (volumetricLightAccumulationTexture) { - GLDEBUG(glDeleteTextures(1, &volumetricLightAccumulationTexture)); - volumetricLightAccumulationTexture = 0; - } - - if (volumetricLightAccumulationBuffer) { - GLDEBUG(glDeleteFramebuffers(1, &volumetricLightAccumulationBuffer)); - volumetricLightAccumulationBuffer = 0; - } -} - -void KRCamera::renderPost() -{ - // Disable alpha blending - GLDEBUG(glDisable(GL_BLEND)); - - -/* - FINDME - Determine if we still need this... - - static const GLfloat squareVerticesShadow[3][8] = {{ - -1.0f, -1.0f, - -0.60f, -1.0f, - -1.0f, -0.60f, - -0.60f, -0.60f, - },{ - -0.50f, -1.0f, - -0.10f, -1.0f, - -0.50f, -0.60f, - -0.10f, -0.60f, - },{ - 0.00f, -1.0f, - 0.40f, -1.0f, - 0.00f, -0.60f, - 0.40f, -0.60f, - }}; - */ - - - GLDEBUG(glViewport(0, 0, m_viewport.getSize().x, m_viewport.getSize().y)); - GLDEBUG(glDisable(GL_DEPTH_TEST)); - KRShader *postShader = m_pContext->getShaderManager()->getShader("PostShader", this, std::vector(), std::vector(), std::vector(), 0, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, KRNode::RENDER_PASS_FORWARD_TRANSPARENT); - - Vector3 rim_color; - getContext().getShaderManager()->selectShader(*this, postShader, m_viewport, Matrix4(), std::vector(), std::vector(), std::vector(), 0, KRNode::RENDER_PASS_FORWARD_TRANSPARENT, rim_color, 0.0f, m_fade_color); - - m_pContext->getTextureManager()->selectTexture(GL_TEXTURE_2D, 0, compositeDepthTexture); - m_pContext->getTextureManager()->selectTexture(GL_TEXTURE_2D, 1, compositeColorTexture); - - if(settings.volumetric_environment_enable) { - m_pContext->getTextureManager()->selectTexture(GL_TEXTURE_2D, 2, volumetricLightAccumulationTexture); - } - - // Update attribute values. - m_pContext->getMeshManager()->bindVBO(&getContext().getMeshManager()->KRENGINE_VBO_DATA_2D_SQUARE_VERTICES, 1.0f); - - GLDEBUG(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)); - - m_pContext->getTextureManager()->selectTexture(GL_TEXTURE_2D, 0, 0); - m_pContext->getTextureManager()->selectTexture(GL_TEXTURE_2D, 1, 0); - - -// if(bShowShadowBuffer) { -// KRShader *blitShader = m_pContext->getShaderManager()->getShader("simple_blit", this, false, false, false, 0, false, false, false, false, false, false, false, false, false, false, false, false, false, KRNode::RENDER_PASS_FORWARD_TRANSPARENT); -// -// for(int iShadow=0; iShadow < m_cShadowBuffers; iShadow++) { -// Matrix4 viewMatrix = Matrix4(); -// viewMatrix.scale(0.20, 0.20, 0.20); -// viewMatrix.translate(-0.70, 0.70 - 0.45 * iShadow, 0.0); -// getContext().getShaderManager()->selectShader(blitShader, KRViewport(getViewportSize(), viewMatrix, Matrix4()), shadowViewports, Matrix4(), Vector3(), NULL, 0, KRNode::RENDER_PASS_FORWARD_TRANSPARENT); -// m_pContext->getTextureManager()->selectTexture(1, NULL); -// m_pContext->getMeshManager()->bindVBO(&getContext().getMeshManager()->KRENGINE_VBO_DATA_2D_SQUARE_VERTICES); -// m_pContext->getTextureManager()->_setActiveTexture(0); -// GLDEBUG(glBindTexture(GL_TEXTURE_2D, shadowDepthTexture[iShadow])); -//#if GL_EXT_shadow_samplers -// GLDEBUG(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_EXT, GL_NONE)); // TODO - Detect GL_EXT_shadow_samplers and only activate if available -//#endif -// GLDEBUG(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)); -//#if GL_EXT_shadow_samplers -// GLDEBUG(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_EXT, GL_COMPARE_REF_TO_TEXTURE_EXT)); // TODO - Detect GL_EXT_shadow_samplers and only activate if available -//#endif -// } -// -// m_pContext->getTextureManager()->selectTexture(0, NULL); -// m_pContext->getTextureManager()->_setActiveTexture(0); -// GLDEBUG(glBindTexture(GL_TEXTURE_2D, 0)); -// } - - const char *szText = settings.m_debug_text.c_str(); - - std::string debug_text; - if(settings.debug_display != KRRenderSettings::KRENGINE_DEBUG_DISPLAY_NONE) { - debug_text = getDebugText();; - if(debug_text.length() > 0) { - szText = debug_text.c_str(); - } - } - - if(*szText) { - int row_count = 1; - const int MAX_TABS = 5; - const int TAB_EXTRA = 2; - int tab_cols[MAX_TABS] = {0, 0, 0, 0, 0}; - int iCol = 0; - int iTab = 0; - const char *pChar = szText; - while(*pChar) { - char c = *pChar++; - if(c == '\n') { - row_count++; - iCol = 0; - iTab = 0; - } else if(c == '\t') { - iCol = 0; - iTab++; - } else { - iCol++; - if(iCol > tab_cols[iTab]) tab_cols[iTab] = iCol; - } - } - - iCol = 0; - for(iTab=0; iTab < MAX_TABS; iTab++) { - iCol += tab_cols[iTab] + TAB_EXTRA; - tab_cols[iTab] = iCol; - } - - const int DEBUG_TEXT_COLUMNS = 256; - const int DEBUG_TEXT_ROWS = 128; - - if(m_debug_text_vertices.getSize() == 0) { - m_debug_text_vertices.expand(sizeof(DebugTextVertexData) * DEBUG_TEXT_COLUMNS * DEBUG_TEXT_ROWS * 6); - } - int vertex_count = 0; - - m_debug_text_vertices.lock(); - DebugTextVertexData *vertex_data = (DebugTextVertexData *)m_debug_text_vertices.getStart(); - - pChar = szText; - float dScaleX = 2.0 / (1024 / 16); - float dScaleY = 2.0 / (768 / 16); - float dTexScale = 1.0 / 16.0; - int iRow = row_count - 1; iCol = 0; iTab = 0; - while(*pChar) { - char c = *pChar++; - if(c == '\n') { - iCol = 0; - iTab = 0; - iRow--; - } else if(c == '\t') { - iCol = tab_cols[iTab++]; - } else { - if(iCol < DEBUG_TEXT_COLUMNS && iRow < DEBUG_TEXT_ROWS) { - int iChar = c - '\0'; - int iTexCol = iChar % 16; - int iTexRow = 15 - (iChar - iTexCol) / 16; - - Vector2 top_left_pos = Vector2(-1.0f + dScaleX * iCol, dScaleY * iRow - 1.0); - Vector2 bottom_right_pos = Vector2(-1.0 + dScaleX * (iCol + 1), dScaleY * iRow + dScaleY - 1.0); - top_left_pos += Vector2(1.0f / 2048.0f * 0.5f, 1.0f / 1536.0f * 0.5f); - bottom_right_pos += Vector2(1.0f / 2048.0f * 0.5f, 1.0f / 1536.0f * 0.5f); - Vector2 top_left_uv = Vector2(dTexScale * iTexCol, dTexScale * iTexRow); - Vector2 bottom_right_uv = Vector2(dTexScale * iTexCol + dTexScale, dTexScale * iTexRow + dTexScale); - - vertex_data[vertex_count].x = top_left_pos.x; - vertex_data[vertex_count].y = top_left_pos.y; - vertex_data[vertex_count].z = 0.0f; - vertex_data[vertex_count].u = top_left_uv.x; - vertex_data[vertex_count].v = top_left_uv.y; - vertex_count++; - - vertex_data[vertex_count].x = bottom_right_pos.x; - vertex_data[vertex_count].y = bottom_right_pos.y; - vertex_data[vertex_count].z = 0.0f; - vertex_data[vertex_count].u = bottom_right_uv.x; - vertex_data[vertex_count].v = bottom_right_uv.y; - vertex_count++; - - vertex_data[vertex_count].x = top_left_pos.x; - vertex_data[vertex_count].y = bottom_right_pos.y; - vertex_data[vertex_count].z = 0.0f; - vertex_data[vertex_count].u = top_left_uv.x; - vertex_data[vertex_count].v = bottom_right_uv.y; - vertex_count++; - - - vertex_data[vertex_count].x = top_left_pos.x; - vertex_data[vertex_count].y = top_left_pos.y; - vertex_data[vertex_count].z = 0.0f; - vertex_data[vertex_count].u = top_left_uv.x; - vertex_data[vertex_count].v = top_left_uv.y; - vertex_count++; - - vertex_data[vertex_count].x = bottom_right_pos.x; - vertex_data[vertex_count].y = top_left_pos.y; - vertex_data[vertex_count].z = 0.0f; - vertex_data[vertex_count].u = bottom_right_uv.x; - vertex_data[vertex_count].v = top_left_uv.y; - vertex_count++; - - vertex_data[vertex_count].x = bottom_right_pos.x; - vertex_data[vertex_count].y = bottom_right_pos.y; - vertex_data[vertex_count].z = 0.0f; - vertex_data[vertex_count].u = bottom_right_uv.x; - vertex_data[vertex_count].v = bottom_right_uv.y; - vertex_count++; - } - - iCol++; - } - } - - - // Disable backface culling - GLDEBUG(glDisable(GL_CULL_FACE)); - - // Disable z-buffer write - GLDEBUG(glDepthMask(GL_FALSE)); - - // Disable z-buffer test - GLDEBUG(glDisable(GL_DEPTH_TEST)); -// GLDEBUG(glDepthRangef(0.0, 1.0)); - - // Enable alpha blending - GLDEBUG(glEnable(GL_BLEND)); - GLDEBUG(glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA)); - - KRShader *fontShader = m_pContext->getShaderManager()->getShader("debug_font", this, std::vector(), std::vector(), std::vector(), 0, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, KRNode::RENDER_PASS_FORWARD_TRANSPARENT); - getContext().getShaderManager()->selectShader(*this, fontShader, m_viewport, Matrix4(), std::vector(), std::vector(), std::vector(), 0, KRNode::RENDER_PASS_FORWARD_TRANSPARENT, Vector3::Zero(), 0.0f, Vector4::Zero()); - - m_pContext->getTextureManager()->selectTexture(0, m_pContext->getTextureManager()->getTexture("font"), 0.0f, KRTexture::TEXTURE_USAGE_UI); - - KRDataBlock index_data; - m_pContext->getMeshManager()->bindVBO(m_debug_text_vertices, index_data, (1 << KRMesh::KRENGINE_ATTRIB_VERTEX) | (1 << KRMesh::KRENGINE_ATTRIB_TEXUVA), true, 1.0f); - - GLDEBUG(glDrawArrays(GL_TRIANGLES, 0, vertex_count)); - - // Re-enable z-buffer write - GLDEBUG(glDepthMask(GL_TRUE)); - - m_debug_text_vertices.unlock(); - - } else { - if(m_debug_text_vertices.getSize() > 0) { - m_debug_text_vertices = KRDataBlock(); - } - } -} - - -std::string KRCamera::getDebugText() -{ - std::stringstream stream; - stream.precision(std::numeric_limits::digits10); - - - - uint64_t fps = 0; - if(m_frame_times_filled == KRAKEN_FPS_AVERAGE_FRAME_COUNT) { - for(int i=0; i < KRAKEN_FPS_AVERAGE_FRAME_COUNT; i++) { - fps += m_frame_times[i]; - } - fps = 1000000 / (fps / KRAKEN_FPS_AVERAGE_FRAME_COUNT); // Order of division chosen to prevent overflow - } - - switch(settings.debug_display) { - case KRRenderSettings::KRENGINE_DEBUG_DISPLAY_NONE: // ----====---- No debug display ----====---- - break; - - case KRRenderSettings::KRENGINE_DEBUG_DISPLAY_TIME: // ----====---- Time / FPS ----====---- - { - if(fps > 0) { - stream << "FPS\t" << fps; - } - } - break; - - case KRRenderSettings::KRENGINE_DEBUG_DISPLAY_MEMORY: // ----====---- Memory Utilization ----=====---- - { -#if defined(__APPLE__) - // ---- CPU Memory ---- - - struct task_basic_info info; - mach_msg_type_number_t size = sizeof(info); - kern_return_t kerr = task_info(mach_task_self(), - TASK_BASIC_INFO, - (task_info_t)&info, - &size); - if( kerr == KERN_SUCCESS ) { - stream << "\tResident\tVirtual\tTotal"; - stream << "\nCPU\t" << (info.resident_size / 1024 / 1024) << " MB\t" << (info.virtual_size / 1024 / 1024) << " MB\t" << ((info.resident_size + info.virtual_size) / 1024 / 1024) << " MB"; - } else { - stream << "\nERROR: Could not get CPU memory utilization."; - } - - - mach_port_t host_port = mach_host_self(); - mach_msg_type_number_t host_size = sizeof(vm_statistics_data_t) / sizeof(integer_t); - vm_size_t pagesize = 0; - vm_statistics_data_t vm_stat; - if(host_page_size(host_port, &pagesize) != KERN_SUCCESS) { - stream << "\n\nERROR: Could not get VM page size."; - } else if(host_statistics(host_port, HOST_VM_INFO, (host_info_t)&vm_stat, &host_size) != KERN_SUCCESS) { - stream << "\n\nERROR: Could not get VM stats."; - } else { - stream << "\n\n\n\tWired\tActive\tInactive\tFree\tTotal"; - stream << "\nVM\t"; - stream << (vm_stat.wire_count * pagesize / 1024 / 1024) << " MB\t"; - stream << (vm_stat.active_count * pagesize / 1024 / 1024) << " MB\t"; - stream << (vm_stat.inactive_count * pagesize / 1024 / 1024) << " MB\t"; - stream << (vm_stat.free_count * pagesize / 1024 / 1024) << " MB\t"; - stream << ((vm_stat.wire_count + vm_stat.active_count + vm_stat.inactive_count) * pagesize / 1024 / 1024) << " MB"; - } -#endif // defined(__APPLE__) - - // ---- GPU Memory ---- - int texture_count_active = m_pContext->getTextureManager()->getActiveTextures().size(); - int texture_count = texture_count_active; - long texture_mem_active = m_pContext->getTextureManager()->getMemActive(); - long texture_mem_used = m_pContext->getTextureManager()->getMemUsed(); - long texture_mem_throughput = m_pContext->getTextureManager()->getMemoryTransferedThisFrame(); - - int vbo_count_active = m_pContext->getMeshManager()->getActiveVBOCount(); - long vbo_mem_active = m_pContext->getMeshManager()->getMemActive(); - long vbo_mem_used = m_pContext->getMeshManager()->getMemUsed(); - long vbo_mem_throughput = m_pContext->getMeshManager()->getMemoryTransferedThisFrame(); - - long total_mem_active = texture_mem_active + vbo_mem_active; - long total_mem_used = texture_mem_used + vbo_mem_used; - long total_mem_throughput = texture_mem_throughput + vbo_mem_throughput; - - stream << "\n\n\n\t# Active\t# Used\tActive\tUsed\tThroughput\n"; - - stream << "Textures\t" << texture_count_active << "\t" << texture_count << "\t" << (texture_mem_active / 1024) << " KB\t" << (texture_mem_used / 1024) << " KB\t" << (texture_mem_throughput / 1024) << " KB / frame\n"; - stream << "VBO's\t" << vbo_count_active << "\t" << vbo_count_active << "\t" << (vbo_mem_active / 1024) <<" KB\t" << (vbo_mem_used / 1024) << " KB\t" << (vbo_mem_throughput / 1024) << " KB / frame\n"; - stream << "\nGPU Total\t\t\t" << (total_mem_active / 1024) << " KB\t" << (total_mem_used / 1024) << " KB\t" << (total_mem_throughput / 1024) << " KB / frame"; - } - break; - - case KRRenderSettings::KRENGINE_DEBUG_DISPLAY_TEXTURES: // ----====---- List Active Textures ----====---- - { - bool first = true; - int texture_count = 0; - std::set active_textures = m_pContext->getTextureManager()->getActiveTextures(); - for(std::set::iterator itr=active_textures.begin(); itr != active_textures.end(); itr++) { - KRTexture *texture = *itr; - if(first) { - first = false; - } else { - stream << "\n"; - } - stream << texture->getName(); - stream << "\t"; - stream << texture->getMemSize() / 1024; - stream << " KB"; - stream << "\t"; - stream << texture->getMaxMipMap(); - if(texture->hasMipmaps() && texture->getCurrentLodMaxDim() != texture->getMaxMipMap()) { - stream << " px => "; - stream << texture->getCurrentLodMaxDim(); - } - stream << " px"; - texture_count++; - } - - stream << "\n\nTOTAL: "; - stream << texture_count; - stream << " textures\t"; - stream << (m_pContext->getTextureManager()->getMemActive() / 1024) << " KB"; - } - break; - - case KRRenderSettings::KRENGINE_DEBUG_DISPLAY_DRAW_CALLS: // ----====---- List Draw Calls ----====---- - { - std::vector draw_calls = m_pContext->getMeshManager()->getDrawCalls(); - - long draw_call_count = 0; - long vertex_count = 0; - stream << "\tVerts\tPass\tObject\tMaterial"; - for(std::vector::iterator itr = draw_calls.begin(); itr != draw_calls.end(); itr++) { - draw_call_count++; - stream << "\n" << draw_call_count << "\t" << (*itr).vertex_count << "\t"; - switch((*itr).pass) { - case KRNode::RENDER_PASS_FORWARD_OPAQUE: - stream << "opaq"; - break; - case KRNode::RENDER_PASS_DEFERRED_GBUFFER: - stream << "d gb"; - break; - case KRNode::RENDER_PASS_DEFERRED_LIGHTS: - stream << "d light"; - break; - case KRNode::RENDER_PASS_DEFERRED_OPAQUE: - stream << "d opaq"; - break; - case KRNode::RENDER_PASS_FORWARD_TRANSPARENT: - stream << "trans"; - break; - case KRNode::RENDER_PASS_PARTICLE_OCCLUSION: - stream << "p occl"; - break; - case KRNode::RENDER_PASS_ADDITIVE_PARTICLES: - stream << "a part"; - break; - case KRNode::RENDER_PASS_VOLUMETRIC_EFFECTS_ADDITIVE: - stream << "vol add"; - break; - case KRNode::RENDER_PASS_GENERATE_SHADOWMAPS: - stream << "g shadow"; - break; - case KRNode::RENDER_PASS_SHADOWMAP: - stream << "shadow"; - break; - default: - // Suppress warnings - break; - } - stream << "\t" << (*itr).object_name << "\t" << (*itr).material_name; - vertex_count += (*itr).vertex_count; - } - stream << "\n\n\t\tTOTAL:\t" << draw_call_count << " draw calls\t" << vertex_count << " vertices"; - } - break; - case KRRenderSettings::KRENGINE_DEBUG_DISPLAY_OCTREE: - stream << "Octree Visualization"; - break; - case KRRenderSettings::KRENGINE_DEBUG_DISPLAY_COLLIDERS: - stream << "Collider Visualization"; - break; - case KRRenderSettings::KRENGINE_DEBUG_DISPLAY_BONES: - stream << "Bone Visualization"; - case KRRenderSettings::KRENGINE_DEBUG_DISPLAY_SIREN_REVERB_ZONES: - stream << "Siren - Reverb Zones"; - break; - case KRRenderSettings::KRENGINE_DEBUG_DISPLAY_SIREN_AMBIENT_ZONES: - stream << "Siren - Ambient Zones"; - break; - case KRRenderSettings::KRENGINE_DEBUG_DISPLAY_NUMBER: - // Suppress warning - break; - } - return stream.str(); -} - - -const KRViewport &KRCamera::getViewport() const -{ - return m_viewport; -} - - -Vector2 KRCamera::getDownsample() -{ - return m_downsample; -} - -void KRCamera::setDownsample(float v) -{ - m_downsample = v; -} - -void KRCamera::setFadeColor(const Vector4 &fade_color) -{ - m_fade_color = fade_color; -} - -Vector4 KRCamera::getFadeColor() -{ - return m_fade_color; -} +// +// KRCamera.cpp +// KREngine +// +// Copyright 2012 Kearwood Gilbert. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other materials +// provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// The views and conclusions contained in the software and documentation are those of the +// authors and should not be interpreted as representing official policies, either expressed +// or implied, of Kearwood Gilbert. +// + +#include "KREngine-common.h" +#include "KRCamera.h" +#include "KRStockGeometry.h" +#include "KRDirectionalLight.h" + +KRCamera::KRCamera(KRScene &scene, std::string name) : KRNode(scene, name) { + m_last_frame_start = 0; + + m_particlesAbsoluteTime = 0.0f; + m_backingWidth = 0; + m_backingHeight = 0; + volumetricBufferWidth = 0; + volumetricBufferHeight = 0; + m_pSkyBoxTexture = NULL; + + compositeDepthTexture = 0; + compositeColorTexture = 0; + lightAccumulationTexture = 0; + compositeFramebuffer = 0; + lightAccumulationBuffer = 0; + + volumetricLightAccumulationBuffer = 0; + volumetricLightAccumulationTexture = 0; + m_frame_times_filled = 0; + m_downsample = Vector2::One(); + + m_fade_color = Vector4::Zero(); +} + +KRCamera::~KRCamera() { + destroyBuffers(); +} + +std::string KRCamera::getElementName() { + return "camera"; +} + +tinyxml2::XMLElement *KRCamera::saveXML( tinyxml2::XMLNode *parent) +{ + tinyxml2::XMLElement *e = KRNode::saveXML(parent); + e->SetAttribute("skybox", m_skyBox.c_str()); + + return e; +} + + +void KRCamera::loadXML(tinyxml2::XMLElement *e) +{ + KRNode::loadXML(e); + const char *szSkyBoxName = e->Attribute("skybox"); + m_skyBox = szSkyBoxName ? szSkyBoxName : ""; +} + +void KRCamera::setSkyBox(const std::string &skyBox) +{ + m_pSkyBoxTexture = NULL; + m_skyBox = skyBox; + +} + +const std::string KRCamera::getSkyBox() const +{ + return m_skyBox; +} + +void KRCamera::renderFrame(GLint defaultFBO, GLint renderBufferWidth, GLint renderBufferHeight) +{ + // ----====---- Record timing information for measuring FPS ----====---- + uint64_t current_time = m_pContext->getAbsoluteTimeMilliseconds(); + if(m_last_frame_start != 0) { + m_frame_times[m_pContext->getCurrentFrame() % KRAKEN_FPS_AVERAGE_FRAME_COUNT] = (int)(current_time - m_last_frame_start); + if(m_frame_times_filled < KRAKEN_FPS_AVERAGE_FRAME_COUNT) m_frame_times_filled++; + } + m_last_frame_start = current_time; + + createBuffers(renderBufferWidth, renderBufferHeight); + + KRScene &scene = getScene(); + + Matrix4 modelMatrix = getModelMatrix(); + Matrix4 viewMatrix = Matrix4::LookAt(Matrix4::Dot(modelMatrix, Vector3::Zero()), Matrix4::Dot(modelMatrix, Vector3::Forward()), Vector3::Normalize(Matrix4::DotNoTranslate(modelMatrix, Vector3::Up()))); + + //Matrix4 viewMatrix = Matrix4::Invert(getModelMatrix()); + + settings.setViewportSize(Vector2::Create(m_backingWidth, m_backingHeight)); + Matrix4 projectionMatrix; + projectionMatrix.perspective(settings.perspective_fov, settings.m_viewportSize.x / settings.m_viewportSize.y, settings.perspective_nearz, settings.perspective_farz); + m_viewport = KRViewport(settings.getViewportSize(), viewMatrix, projectionMatrix); + m_viewport.setLODBias(settings.getLODBias()); + + Vector3 vecCameraDirection = m_viewport.getCameraDirection(); + + + scene.updateOctree(m_viewport); + + // ----====---- Pre-stream resources ----====---- + scene.render(this, m_viewport.getVisibleBounds(), m_viewport, KRNode::RENDER_PASS_PRESTREAM, true); + + // ----====---- Generate Shadowmaps for Lights ----====---- + if(settings.m_cShadowBuffers > 0) { + GL_PUSH_GROUP_MARKER("Generate Shadowmaps"); + + scene.render(this, m_viewport.getVisibleBounds(), m_viewport, KRNode::RENDER_PASS_GENERATE_SHADOWMAPS, false /*settings.bEnableDeferredLighting*/); + GLDEBUG(glViewport(0, 0, m_viewport.getSize().x, m_viewport.getSize().y)); + GL_POP_GROUP_MARKER; + } + + if(settings.bEnableDeferredLighting) { + + // ----====---- Opaque Geometry, Deferred rendering Pass 1 ----====---- + + GL_PUSH_GROUP_MARKER("Deferred Lighting - Pass 1 (Opaque)"); + + // Set render target + GLDEBUG(glBindFramebuffer(GL_FRAMEBUFFER, compositeFramebuffer)); + + +#if GL_EXT_discard_framebuffer + GLenum attachments[2] = {GL_DEPTH_ATTACHMENT, GL_COLOR_ATTACHMENT0}; + GLDEBUG(glDiscardFramebufferEXT(GL_FRAMEBUFFER, 2, attachments)); +#endif + + // Enable z-buffer write + GLDEBUG(glDepthMask(GL_TRUE)); + + GLDEBUG(glClearColor(0.0f, 0.0f, 0.0f, 0.0f)); + GLDEBUG(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)); + + // Enable backface culling + GLDEBUG(glCullFace(GL_BACK)); + GLDEBUG(glEnable(GL_CULL_FACE)); + + // Enable z-buffer test + GLDEBUG(glEnable(GL_DEPTH_TEST)); + GLDEBUG(glDepthFunc(GL_LEQUAL)); + GLDEBUG(glDepthRangef(0.0, 1.0)); + + // Disable alpha blending + GLDEBUG(glDisable(GL_BLEND)); + + // Render the geometry + scene.render(this, m_viewport.getVisibleBounds(), m_viewport, KRNode::RENDER_PASS_DEFERRED_GBUFFER, false); + + + GL_POP_GROUP_MARKER; + + + + // ----====---- Opaque Geometry, Deferred rendering Pass 2 ----====---- + + GL_PUSH_GROUP_MARKER("Deferred Lighting - Pass 2 (Opaque)"); + + // Set render target + GLDEBUG(glBindFramebuffer(GL_FRAMEBUFFER, lightAccumulationBuffer)); + GLDEBUG(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, compositeDepthTexture, 0)); + GLDEBUG(glViewport(0, 0, m_viewport.getSize().x * m_downsample.x, m_viewport.getSize().y * m_downsample.y)); + GLDEBUG(glClearColor(0.0f, 0.0f, 0.0f, 0.0f)); + GLDEBUG(glClear(GL_COLOR_BUFFER_BIT)); + + // Enable additive blending + GLDEBUG(glEnable(GL_BLEND)); + GLDEBUG(glBlendFunc(GL_ONE, GL_ONE)); + + // Disable z-buffer write + GLDEBUG(glDepthMask(GL_FALSE)); + + // Set source to buffers from pass 1 + m_pContext->getTextureManager()->selectTexture(GL_TEXTURE_2D, 6, compositeColorTexture); + m_pContext->getTextureManager()->selectTexture(GL_TEXTURE_2D, 7, compositeDepthTexture); + + + // Render the geometry + scene.render(this, m_viewport.getVisibleBounds(), m_viewport, KRNode::RENDER_PASS_DEFERRED_LIGHTS, false); + + GL_POP_GROUP_MARKER; + + // ----====---- Opaque Geometry, Deferred rendering Pass 3 ----====---- + + GL_PUSH_GROUP_MARKER("Deferred Lighting - Pass 3 (Opaque)"); + + // Set render target + GLDEBUG(glBindFramebuffer(GL_FRAMEBUFFER, compositeFramebuffer)); + GLDEBUG(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, compositeDepthTexture, 0)); + + // Disable alpha blending + GLDEBUG(glDisable(GL_BLEND)); + + GLDEBUG(glClearColor(0.0f, 0.0f, 0.0f, 1.0f)); + GLDEBUG(glClear(GL_COLOR_BUFFER_BIT)); + + // Set source to buffers from pass 2 + m_pContext->getTextureManager()->selectTexture(GL_TEXTURE_2D, 6, lightAccumulationTexture); + + // Enable backface culling + GLDEBUG(glCullFace(GL_BACK)); + GLDEBUG(glEnable(GL_CULL_FACE)); + + // Enable z-buffer test + GLDEBUG(glEnable(GL_DEPTH_TEST)); + GLDEBUG(glDepthFunc(GL_LEQUAL)); + GLDEBUG(glDepthRangef(0.0, 1.0)); + + // Enable z-buffer write + GLDEBUG(glDepthMask(GL_TRUE)); + + // Render the geometry + // TODO: At this point, we only want to render octree nodes that produced fragments during the 1st pass into the GBuffer + scene.render(this, m_viewport.getVisibleBounds(), m_viewport, KRNode::RENDER_PASS_DEFERRED_OPAQUE, false); + + // Deactivate source buffer texture units + m_pContext->getTextureManager()->selectTexture(GL_TEXTURE_2D, 6, 0); + m_pContext->getTextureManager()->selectTexture(GL_TEXTURE_2D, 7, 0); + + GL_POP_GROUP_MARKER; + } else { + // ----====---- Opaque Geometry, Forward Rendering ----====---- + + GL_PUSH_GROUP_MARKER("Forward Rendering - Opaque"); + + // Set render target + GLDEBUG(glBindFramebuffer(GL_FRAMEBUFFER, compositeFramebuffer)); + GLDEBUG(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, compositeDepthTexture, 0)); + GLDEBUG(glViewport(0, 0, m_viewport.getSize().x * m_downsample.x, m_viewport.getSize().y * m_downsample.y)); + + // Disable alpha blending + GLDEBUG(glDisable(GL_BLEND)); + + GLDEBUG(glClearColor(0.0f, 0.0f, 0.0f, 1.0f)); + + // Enable z-buffer write + GLDEBUG(glDepthMask(GL_TRUE)); + + +#if GL_EXT_discard_framebuffer + GLenum attachments[2] = {GL_DEPTH_ATTACHMENT, GL_COLOR_ATTACHMENT0}; + GLDEBUG(glDiscardFramebufferEXT(GL_FRAMEBUFFER, 2, attachments)); +#endif + GLDEBUG(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)); + + // Enable backface culling + GLDEBUG(glCullFace(GL_BACK)); + GLDEBUG(glEnable(GL_CULL_FACE)); + + + + // Enable z-buffer test + GLDEBUG(glEnable(GL_DEPTH_TEST)); + GLDEBUG(glDepthFunc(GL_LEQUAL)); + GLDEBUG(glDepthRangef(0.0, 1.0)); + + + + // Render the geometry + scene.render(this, m_viewport.getVisibleBounds(), m_viewport, KRNode::RENDER_PASS_FORWARD_OPAQUE, false); + + GL_POP_GROUP_MARKER; + } + + // ----====---- Sky Box ----====---- + + + GL_PUSH_GROUP_MARKER("Sky Box"); + + // Set render target + GLDEBUG(glBindFramebuffer(GL_FRAMEBUFFER, compositeFramebuffer)); + GLDEBUG(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, compositeDepthTexture, 0)); + + // Disable backface culling + GLDEBUG(glDisable(GL_CULL_FACE)); + + // Disable z-buffer write + GLDEBUG(glDepthMask(GL_FALSE)); + + // Enable z-buffer test + GLDEBUG(glEnable(GL_DEPTH_TEST)); + GLDEBUG(glDepthFunc(GL_LEQUAL)); + GLDEBUG(glDepthRangef(0.0, 1.0)); + + if(!m_pSkyBoxTexture && m_skyBox.length()) { + m_pSkyBoxTexture = getContext().getTextureManager()->getTextureCube(m_skyBox.c_str()); + } + + if(m_pSkyBoxTexture) { + getContext().getShaderManager()->selectShader("sky_box", *this, std::vector(), std::vector(), std::vector(), 0, m_viewport, Matrix4(), false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, KRNode::RENDER_PASS_FORWARD_OPAQUE, Vector3::Zero(), 0.0f, Vector4::Zero()); + + getContext().getTextureManager()->selectTexture(0, m_pSkyBoxTexture, 0.0f, KRTexture::TEXTURE_USAGE_SKY_CUBE); + + // Render a full screen quad + m_pContext->getMeshManager()->bindVBO(&getContext().getMeshManager()->KRENGINE_VBO_DATA_2D_SQUARE_VERTICES, 1.0f); + GLDEBUG(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)); + } + + GL_POP_GROUP_MARKER; + + + // ----====---- Transparent Geometry, Forward Rendering ----====---- + + GL_PUSH_GROUP_MARKER("Forward Rendering - Transparent"); + +// Note: These parameters have already been set up by the skybox render above +// +// // Set render target +// GLDEBUG(glBindFramebuffer(GL_FRAMEBUFFER, compositeFramebuffer)); +// GLDEBUG(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, compositeDepthTexture, 0)); +// +// // Disable backface culling +// GLDEBUG(glDisable(GL_CULL_FACE)); +// + // Enable backface culling + GLDEBUG(glCullFace(GL_BACK)); + GLDEBUG(glEnable(GL_CULL_FACE)); + + // Disable z-buffer write + GLDEBUG(glDepthMask(GL_FALSE)); +// +// // Enable z-buffer test +// GLDEBUG(glEnable(GL_DEPTH_TEST)); +// GLDEBUG(glDepthFunc(GL_LEQUAL)); +// GLDEBUG(glDepthRangef(0.0, 1.0)); + + // Enable alpha blending + GLDEBUG(glEnable(GL_BLEND)); + GLDEBUG(glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA)); + + // Render all transparent geometry + scene.render(this, m_viewport.getVisibleBounds(), m_viewport, KRNode::RENDER_PASS_FORWARD_TRANSPARENT, false); + + GL_POP_GROUP_MARKER; + + // ----====---- Particle Occlusion Tests ----====---- + + GL_PUSH_GROUP_MARKER("Particle Occlusion Tests"); + + // Set render target + GLDEBUG(glBindFramebuffer(GL_FRAMEBUFFER, compositeFramebuffer)); + GLDEBUG(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, compositeDepthTexture, 0)); + + // Disable backface culling + GLDEBUG(glDisable(GL_CULL_FACE)); + + // Disable z-buffer write + GLDEBUG(glDepthMask(GL_FALSE)); + + // Enable z-buffer test + GLDEBUG(glEnable(GL_DEPTH_TEST)); + GLDEBUG(glDepthFunc(GL_LEQUAL)); + GLDEBUG(glDepthRangef(0.0, 1.0)); + + // Enable additive blending + GLDEBUG(glEnable(GL_BLEND)); + GLDEBUG(glBlendFunc(GL_ONE, GL_ONE)); + + // ----====---- Perform Occlusion Tests ----====---- + scene.render(this, m_viewport.getVisibleBounds(), m_viewport, RENDER_PASS_PARTICLE_OCCLUSION, false); + + GL_POP_GROUP_MARKER; + + // ----====---- Flares ----====---- + + GL_PUSH_GROUP_MARKER("Additive Particles"); + + // Set render target + GLDEBUG(glBindFramebuffer(GL_FRAMEBUFFER, compositeFramebuffer)); + GLDEBUG(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, compositeDepthTexture, 0)); + + // Disable backface culling + GLDEBUG(glDisable(GL_CULL_FACE)); + + // Disable z-buffer write + GLDEBUG(glDepthMask(GL_FALSE)); + + // Disable z-buffer test + GLDEBUG(glDisable(GL_DEPTH_TEST)); + GLDEBUG(glDepthRangef(0.0, 1.0)); + + // Enable additive blending + GLDEBUG(glEnable(GL_BLEND)); + GLDEBUG(glBlendFunc(GL_ONE, GL_ONE)); + + // Render all flares + scene.render(this, m_viewport.getVisibleBounds(), m_viewport, KRNode::RENDER_PASS_ADDITIVE_PARTICLES, false); + + GL_POP_GROUP_MARKER; + + // ----====---- Volumetric Lighting ----====---- + + if(settings.volumetric_environment_enable) { + + GL_PUSH_GROUP_MARKER("Volumetric Lighting"); + + KRViewport volumetricLightingViewport = KRViewport(Vector2::Create(volumetricBufferWidth, volumetricBufferHeight), m_viewport.getViewMatrix(), m_viewport.getProjectionMatrix()); + + if(settings.volumetric_environment_downsample != 0) { + // Set render target + GLDEBUG(glBindFramebuffer(GL_FRAMEBUFFER, volumetricLightAccumulationBuffer)); + GLDEBUG(glClearColor(0.0f, 0.0f, 0.0f, 0.0f)); + GLDEBUG(glClear(GL_COLOR_BUFFER_BIT)); + + // Disable z-buffer test + GLDEBUG(glDisable(GL_DEPTH_TEST)); + m_pContext->getTextureManager()->selectTexture(GL_TEXTURE_2D, 0, compositeDepthTexture); + + GLDEBUG(glViewport(0, 0, volumetricLightingViewport.getSize().x, volumetricLightingViewport.getSize().y)); + } else { + // Enable z-buffer test + GLDEBUG(glEnable(GL_DEPTH_TEST)); + GLDEBUG(glDepthFunc(GL_LEQUAL)); + GLDEBUG(glDepthRangef(0.0, 1.0)); + } + + scene.render(this, m_viewport.getVisibleBounds(), volumetricLightingViewport, KRNode::RENDER_PASS_VOLUMETRIC_EFFECTS_ADDITIVE, false); + + if(settings.volumetric_environment_downsample != 0) { + // Set render target + GLDEBUG(glBindFramebuffer(GL_FRAMEBUFFER, compositeFramebuffer)); + GLDEBUG(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, compositeDepthTexture, 0)); + + GLDEBUG(glViewport(0, 0, m_viewport.getSize().x, m_viewport.getSize().y)); + } + + GL_POP_GROUP_MARKER; + } + + + + // ----====---- Debug Overlay ----====---- + + GL_PUSH_GROUP_MARKER("Debug Overlays"); + + if(settings.debug_display == KRRenderSettings::KRENGINE_DEBUG_DISPLAY_OCTREE) { + // Enable z-buffer test + GLDEBUG(glEnable(GL_DEPTH_TEST)); + GLDEBUG(glDepthRangef(0.0, 1.0)); + + + // Enable backface culling + GLDEBUG(glCullFace(GL_BACK)); + GLDEBUG(glEnable(GL_CULL_FACE)); + + // Enable additive blending + GLDEBUG(glEnable(GL_BLEND)); + GLDEBUG(glBlendFunc(GL_ONE, GL_ONE)); + + + KRShader *pVisShader = getContext().getShaderManager()->getShader("visualize_overlay", this, std::vector(), std::vector(), std::vector(), 0, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, KRNode::RENDER_PASS_FORWARD_TRANSPARENT); + + m_pContext->getMeshManager()->bindVBO(&getContext().getMeshManager()->KRENGINE_VBO_DATA_3D_CUBE_VERTICES, 1.0f); + for(unordered_map::iterator itr=m_viewport.getVisibleBounds().begin(); itr != m_viewport.getVisibleBounds().end(); itr++) { + Matrix4 matModel = Matrix4(); + matModel.scale((*itr).first.size() * 0.5f); + matModel.translate((*itr).first.center()); + if(getContext().getShaderManager()->selectShader(*this, pVisShader, m_viewport, matModel, std::vector(), std::vector(), std::vector(), 0, KRNode::RENDER_PASS_FORWARD_TRANSPARENT, Vector3::Zero(), 0.0f, Vector4::Zero())) { + GLDEBUG(glDrawArrays(GL_TRIANGLE_STRIP, 0, 14)); + } + } + } + + // Re-enable z-buffer write + GLDEBUG(glDepthMask(GL_TRUE)); + + GL_POP_GROUP_MARKER; + + +// fprintf(stderr, "VBO Mem: %i Kbyte Texture Mem: %i/%i Kbyte (active/total) Shader Handles: %i Visible Bounds: %i Max Texture LOD: %i\n", (int)m_pContext->getMeshManager()->getMemUsed() / 1024, (int)m_pContext->getTextureManager()->getActiveMemUsed() / 1024, (int)m_pContext->getTextureManager()->getMemUsed() / 1024, (int)m_pContext->getShaderManager()->getShaderHandlesUsed(), (int)m_visibleBounds.size(), m_pContext->getTextureManager()->getLODDimCap()); + GL_PUSH_GROUP_MARKER("Post Processing"); + + GLDEBUG(glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO)); + renderPost(); + m_pContext->getMeshManager()->unbindVBO(); + + GL_POP_GROUP_MARKER; + + +#if GL_EXT_discard_framebuffer + + GLenum attachments[2] = {GL_DEPTH_ATTACHMENT, GL_COLOR_ATTACHMENT0}; + GLDEBUG(glDiscardFramebufferEXT(GL_FRAMEBUFFER, 2, attachments)); +#endif +} + + +void KRCamera::createBuffers(GLint renderBufferWidth, GLint renderBufferHeight) { + + if(renderBufferWidth != m_backingWidth || renderBufferHeight != m_backingHeight) { + m_backingWidth = renderBufferWidth; + m_backingHeight = renderBufferHeight; + + if (compositeDepthTexture) { + GLDEBUG(glDeleteTextures(1, &compositeDepthTexture)); + compositeDepthTexture = 0; + } + + if (compositeColorTexture) { + GLDEBUG(glDeleteTextures(1, &compositeColorTexture)); + compositeColorTexture = 0; + } + + if (lightAccumulationTexture) { + GLDEBUG(glDeleteTextures(1, &lightAccumulationTexture)); + lightAccumulationTexture = 0; + } + + if (compositeFramebuffer) { + GLDEBUG(glDeleteFramebuffers(1, &compositeFramebuffer)); + compositeFramebuffer = 0; + } + + if (lightAccumulationBuffer) { + GLDEBUG(glDeleteFramebuffers(1, &lightAccumulationBuffer)); + lightAccumulationBuffer = 0; + } + + + // ===== Create offscreen compositing framebuffer object ===== + GLDEBUG(glGenFramebuffers(1, &compositeFramebuffer)); + GLDEBUG(glBindFramebuffer(GL_FRAMEBUFFER, compositeFramebuffer)); + + // ----- Create texture color buffer for compositeFramebuffer ----- + GLDEBUG(glGenTextures(1, &compositeColorTexture)); + GLDEBUG(glBindTexture(GL_TEXTURE_2D, compositeColorTexture)); + GLDEBUG(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); + GLDEBUG(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); + GLDEBUG(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); // This is necessary for non-power-of-two textures + GLDEBUG(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); // This is necessary for non-power-of-two textures + GLDEBUG(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_backingWidth, m_backingHeight, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL)); + GLDEBUG(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, compositeColorTexture, 0)); + + // ----- Create Depth Texture for compositeFramebuffer ----- + GLDEBUG(glGenTextures(1, &compositeDepthTexture)); + GLDEBUG(glBindTexture(GL_TEXTURE_2D, compositeDepthTexture)); + GLDEBUG(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)); + GLDEBUG(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)); + GLDEBUG(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); // This is necessary for non-power-of-two textures + GLDEBUG(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); // This is necessary for non-power-of-two textures + GLDEBUG(glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, m_backingWidth, m_backingHeight, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL)); + //GLDEBUG(glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT16, m_backingWidth, m_backingHeight, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL)); + //GLDEBUG(glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24_OES, m_backingWidth, m_backingHeight)); + GLDEBUG(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, compositeDepthTexture, 0)); + + // ===== Create offscreen compositing framebuffer object ===== + GLDEBUG(glGenFramebuffers(1, &lightAccumulationBuffer)); + GLDEBUG(glBindFramebuffer(GL_FRAMEBUFFER, lightAccumulationBuffer)); + + // ----- Create texture color buffer for compositeFramebuffer ----- + GLDEBUG(glGenTextures(1, &lightAccumulationTexture)); + GLDEBUG(glBindTexture(GL_TEXTURE_2D, lightAccumulationTexture)); + GLDEBUG(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); + GLDEBUG(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); + GLDEBUG(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); // This is necessary for non-power-of-two textures + GLDEBUG(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); // This is necessary for non-power-of-two textures + GLDEBUG(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_backingWidth, m_backingHeight, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL)); + GLDEBUG(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, lightAccumulationTexture, 0)); + } + + int targetVolumetricBufferWidth = 0; + int targetVolumetricBufferHeight = 0; + if(settings.volumetric_environment_enable && settings.volumetric_environment_downsample != 0) { + targetVolumetricBufferWidth = renderBufferWidth >> settings.volumetric_environment_downsample; + targetVolumetricBufferHeight = renderBufferHeight >> settings.volumetric_environment_downsample; + } + + + if(targetVolumetricBufferWidth != volumetricBufferWidth || targetVolumetricBufferHeight != volumetricBufferHeight) { + volumetricBufferWidth = targetVolumetricBufferWidth; + volumetricBufferHeight = targetVolumetricBufferHeight; + + if (volumetricLightAccumulationTexture) { + GLDEBUG(glDeleteTextures(1, &volumetricLightAccumulationTexture)); + volumetricLightAccumulationTexture = 0; + } + + if (volumetricLightAccumulationBuffer) { + GLDEBUG(glDeleteFramebuffers(1, &volumetricLightAccumulationBuffer)); + volumetricLightAccumulationBuffer = 0; + } + + + if(targetVolumetricBufferWidth != 0 && targetVolumetricBufferHeight != 0) { + // ===== Create offscreen compositing framebuffer object for volumetric lighting ===== + GLDEBUG(glGenFramebuffers(1, &volumetricLightAccumulationBuffer)); + GLDEBUG(glBindFramebuffer(GL_FRAMEBUFFER, volumetricLightAccumulationBuffer)); + + // ----- Create texture color buffer for compositeFramebuffer for volumetric lighting ----- + GLDEBUG(glGenTextures(1, &volumetricLightAccumulationTexture)); + GLDEBUG(glBindTexture(GL_TEXTURE_2D, volumetricLightAccumulationTexture)); + GLDEBUG(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); + GLDEBUG(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); + GLDEBUG(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); // This is necessary for non-power-of-two textures + GLDEBUG(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); // This is necessary for non-power-of-two textures + GLDEBUG(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, volumetricBufferWidth, volumetricBufferHeight, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL)); + GLDEBUG(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, volumetricLightAccumulationTexture, 0)); + } + } +} + +void KRCamera::destroyBuffers() +{ + if (compositeDepthTexture) { + GLDEBUG(glDeleteTextures(1, &compositeDepthTexture)); + compositeDepthTexture = 0; + } + + if (compositeColorTexture) { + GLDEBUG(glDeleteTextures(1, &compositeColorTexture)); + compositeColorTexture = 0; + } + + if (lightAccumulationTexture) { + GLDEBUG(glDeleteTextures(1, &lightAccumulationTexture)); + lightAccumulationTexture = 0; + } + + if (compositeFramebuffer) { + GLDEBUG(glDeleteFramebuffers(1, &compositeFramebuffer)); + compositeFramebuffer = 0; + } + + if (lightAccumulationBuffer) { + GLDEBUG(glDeleteFramebuffers(1, &lightAccumulationBuffer)); + lightAccumulationBuffer = 0; + } + + if (volumetricLightAccumulationTexture) { + GLDEBUG(glDeleteTextures(1, &volumetricLightAccumulationTexture)); + volumetricLightAccumulationTexture = 0; + } + + if (volumetricLightAccumulationBuffer) { + GLDEBUG(glDeleteFramebuffers(1, &volumetricLightAccumulationBuffer)); + volumetricLightAccumulationBuffer = 0; + } +} + +void KRCamera::renderPost() +{ + // Disable alpha blending + GLDEBUG(glDisable(GL_BLEND)); + + +/* + FINDME - Determine if we still need this... + + static const GLfloat squareVerticesShadow[3][8] = {{ + -1.0f, -1.0f, + -0.60f, -1.0f, + -1.0f, -0.60f, + -0.60f, -0.60f, + },{ + -0.50f, -1.0f, + -0.10f, -1.0f, + -0.50f, -0.60f, + -0.10f, -0.60f, + },{ + 0.00f, -1.0f, + 0.40f, -1.0f, + 0.00f, -0.60f, + 0.40f, -0.60f, + }}; + */ + + + GLDEBUG(glViewport(0, 0, m_viewport.getSize().x, m_viewport.getSize().y)); + GLDEBUG(glDisable(GL_DEPTH_TEST)); + KRShader *postShader = m_pContext->getShaderManager()->getShader("PostShader", this, std::vector(), std::vector(), std::vector(), 0, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, KRNode::RENDER_PASS_FORWARD_TRANSPARENT); + + Vector3 rim_color; + getContext().getShaderManager()->selectShader(*this, postShader, m_viewport, Matrix4(), std::vector(), std::vector(), std::vector(), 0, KRNode::RENDER_PASS_FORWARD_TRANSPARENT, rim_color, 0.0f, m_fade_color); + + m_pContext->getTextureManager()->selectTexture(GL_TEXTURE_2D, 0, compositeDepthTexture); + m_pContext->getTextureManager()->selectTexture(GL_TEXTURE_2D, 1, compositeColorTexture); + + if(settings.volumetric_environment_enable) { + m_pContext->getTextureManager()->selectTexture(GL_TEXTURE_2D, 2, volumetricLightAccumulationTexture); + } + + // Update attribute values. + m_pContext->getMeshManager()->bindVBO(&getContext().getMeshManager()->KRENGINE_VBO_DATA_2D_SQUARE_VERTICES, 1.0f); + + GLDEBUG(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)); + + m_pContext->getTextureManager()->selectTexture(GL_TEXTURE_2D, 0, 0); + m_pContext->getTextureManager()->selectTexture(GL_TEXTURE_2D, 1, 0); + + +// if(bShowShadowBuffer) { +// KRShader *blitShader = m_pContext->getShaderManager()->getShader("simple_blit", this, false, false, false, 0, false, false, false, false, false, false, false, false, false, false, false, false, false, KRNode::RENDER_PASS_FORWARD_TRANSPARENT); +// +// for(int iShadow=0; iShadow < m_cShadowBuffers; iShadow++) { +// Matrix4 viewMatrix = Matrix4(); +// viewMatrix.scale(0.20, 0.20, 0.20); +// viewMatrix.translate(-0.70, 0.70 - 0.45 * iShadow, 0.0); +// getContext().getShaderManager()->selectShader(blitShader, KRViewport(getViewportSize(), viewMatrix, Matrix4()), shadowViewports, Matrix4(), Vector3(), NULL, 0, KRNode::RENDER_PASS_FORWARD_TRANSPARENT); +// m_pContext->getTextureManager()->selectTexture(1, NULL); +// m_pContext->getMeshManager()->bindVBO(&getContext().getMeshManager()->KRENGINE_VBO_DATA_2D_SQUARE_VERTICES); +// m_pContext->getTextureManager()->_setActiveTexture(0); +// GLDEBUG(glBindTexture(GL_TEXTURE_2D, shadowDepthTexture[iShadow])); +//#if GL_EXT_shadow_samplers +// GLDEBUG(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_EXT, GL_NONE)); // TODO - Detect GL_EXT_shadow_samplers and only activate if available +//#endif +// GLDEBUG(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)); +//#if GL_EXT_shadow_samplers +// GLDEBUG(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_EXT, GL_COMPARE_REF_TO_TEXTURE_EXT)); // TODO - Detect GL_EXT_shadow_samplers and only activate if available +//#endif +// } +// +// m_pContext->getTextureManager()->selectTexture(0, NULL); +// m_pContext->getTextureManager()->_setActiveTexture(0); +// GLDEBUG(glBindTexture(GL_TEXTURE_2D, 0)); +// } + + const char *szText = settings.m_debug_text.c_str(); + + std::string debug_text; + if(settings.debug_display != KRRenderSettings::KRENGINE_DEBUG_DISPLAY_NONE) { + debug_text = getDebugText();; + if(debug_text.length() > 0) { + szText = debug_text.c_str(); + } + } + + if(*szText) { + int row_count = 1; + const int MAX_TABS = 5; + const int TAB_EXTRA = 2; + int tab_cols[MAX_TABS] = {0, 0, 0, 0, 0}; + int iCol = 0; + int iTab = 0; + const char *pChar = szText; + while(*pChar) { + char c = *pChar++; + if(c == '\n') { + row_count++; + iCol = 0; + iTab = 0; + } else if(c == '\t') { + iCol = 0; + iTab++; + } else { + iCol++; + if(iCol > tab_cols[iTab]) tab_cols[iTab] = iCol; + } + } + + iCol = 0; + for(iTab=0; iTab < MAX_TABS; iTab++) { + iCol += tab_cols[iTab] + TAB_EXTRA; + tab_cols[iTab] = iCol; + } + + const int DEBUG_TEXT_COLUMNS = 256; + const int DEBUG_TEXT_ROWS = 128; + + if(m_debug_text_vertices.getSize() == 0) { + m_debug_text_vertices.expand(sizeof(DebugTextVertexData) * DEBUG_TEXT_COLUMNS * DEBUG_TEXT_ROWS * 6); + } + int vertex_count = 0; + + m_debug_text_vertices.lock(); + DebugTextVertexData *vertex_data = (DebugTextVertexData *)m_debug_text_vertices.getStart(); + + pChar = szText; + float dScaleX = 2.0f / (1024f / 16f); + float dScaleY = 2.0f / (768f / 16f); + float dTexScale = 1.0f / 16.0f; + int iRow = row_count - 1; iCol = 0; iTab = 0; + while(*pChar) { + char c = *pChar++; + if(c == '\n') { + iCol = 0; + iTab = 0; + iRow--; + } else if(c == '\t') { + iCol = tab_cols[iTab++]; + } else { + if(iCol < DEBUG_TEXT_COLUMNS && iRow < DEBUG_TEXT_ROWS) { + int iChar = c - '\0'; + int iTexCol = iChar % 16; + int iTexRow = 15 - (iChar - iTexCol) / 16; + + Vector2 top_left_pos = Vector2::Create(-1.0f + dScaleX * iCol, dScaleY * iRow - 1.0f); + Vector2 bottom_right_pos = Vector2::Create(-1.0 + dScaleX * (iCol + 1), dScaleY * iRow + dScaleY - 1.0f); + top_left_pos += Vector2::Create(1.0f / 2048.0f * 0.5f, 1.0f / 1536.0f * 0.5f); + bottom_right_pos += Vector2::Create(1.0f / 2048.0f * 0.5f, 1.0f / 1536.0f * 0.5f); + Vector2 top_left_uv = Vector2::Create(dTexScale * iTexCol, dTexScale * iTexRow); + Vector2 bottom_right_uv = Vector2::Create(dTexScale * iTexCol + dTexScale, dTexScale * iTexRow + dTexScale); + + vertex_data[vertex_count].x = top_left_pos.x; + vertex_data[vertex_count].y = top_left_pos.y; + vertex_data[vertex_count].z = 0.0f; + vertex_data[vertex_count].u = top_left_uv.x; + vertex_data[vertex_count].v = top_left_uv.y; + vertex_count++; + + vertex_data[vertex_count].x = bottom_right_pos.x; + vertex_data[vertex_count].y = bottom_right_pos.y; + vertex_data[vertex_count].z = 0.0f; + vertex_data[vertex_count].u = bottom_right_uv.x; + vertex_data[vertex_count].v = bottom_right_uv.y; + vertex_count++; + + vertex_data[vertex_count].x = top_left_pos.x; + vertex_data[vertex_count].y = bottom_right_pos.y; + vertex_data[vertex_count].z = 0.0f; + vertex_data[vertex_count].u = top_left_uv.x; + vertex_data[vertex_count].v = bottom_right_uv.y; + vertex_count++; + + + vertex_data[vertex_count].x = top_left_pos.x; + vertex_data[vertex_count].y = top_left_pos.y; + vertex_data[vertex_count].z = 0.0f; + vertex_data[vertex_count].u = top_left_uv.x; + vertex_data[vertex_count].v = top_left_uv.y; + vertex_count++; + + vertex_data[vertex_count].x = bottom_right_pos.x; + vertex_data[vertex_count].y = top_left_pos.y; + vertex_data[vertex_count].z = 0.0f; + vertex_data[vertex_count].u = bottom_right_uv.x; + vertex_data[vertex_count].v = top_left_uv.y; + vertex_count++; + + vertex_data[vertex_count].x = bottom_right_pos.x; + vertex_data[vertex_count].y = bottom_right_pos.y; + vertex_data[vertex_count].z = 0.0f; + vertex_data[vertex_count].u = bottom_right_uv.x; + vertex_data[vertex_count].v = bottom_right_uv.y; + vertex_count++; + } + + iCol++; + } + } + + + // Disable backface culling + GLDEBUG(glDisable(GL_CULL_FACE)); + + // Disable z-buffer write + GLDEBUG(glDepthMask(GL_FALSE)); + + // Disable z-buffer test + GLDEBUG(glDisable(GL_DEPTH_TEST)); +// GLDEBUG(glDepthRangef(0.0, 1.0)); + + // Enable alpha blending + GLDEBUG(glEnable(GL_BLEND)); + GLDEBUG(glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA)); + + KRShader *fontShader = m_pContext->getShaderManager()->getShader("debug_font", this, std::vector(), std::vector(), std::vector(), 0, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, KRNode::RENDER_PASS_FORWARD_TRANSPARENT); + getContext().getShaderManager()->selectShader(*this, fontShader, m_viewport, Matrix4(), std::vector(), std::vector(), std::vector(), 0, KRNode::RENDER_PASS_FORWARD_TRANSPARENT, Vector3::Zero(), 0.0f, Vector4::Zero()); + + m_pContext->getTextureManager()->selectTexture(0, m_pContext->getTextureManager()->getTexture("font"), 0.0f, KRTexture::TEXTURE_USAGE_UI); + + KRDataBlock index_data; + m_pContext->getMeshManager()->bindVBO(m_debug_text_vertices, index_data, (1 << KRMesh::KRENGINE_ATTRIB_VERTEX) | (1 << KRMesh::KRENGINE_ATTRIB_TEXUVA), true, 1.0f); + + GLDEBUG(glDrawArrays(GL_TRIANGLES, 0, vertex_count)); + + // Re-enable z-buffer write + GLDEBUG(glDepthMask(GL_TRUE)); + + m_debug_text_vertices.unlock(); + + } else { + if(m_debug_text_vertices.getSize() > 0) { + m_debug_text_vertices = KRDataBlock(); + } + } +} + + +std::string KRCamera::getDebugText() +{ + std::stringstream stream; + stream.precision(std::numeric_limits::digits10); + + + + uint64_t fps = 0; + if(m_frame_times_filled == KRAKEN_FPS_AVERAGE_FRAME_COUNT) { + for(int i=0; i < KRAKEN_FPS_AVERAGE_FRAME_COUNT; i++) { + fps += m_frame_times[i]; + } + fps = 1000000 / (fps / KRAKEN_FPS_AVERAGE_FRAME_COUNT); // Order of division chosen to prevent overflow + } + + switch(settings.debug_display) { + case KRRenderSettings::KRENGINE_DEBUG_DISPLAY_NONE: // ----====---- No debug display ----====---- + break; + + case KRRenderSettings::KRENGINE_DEBUG_DISPLAY_TIME: // ----====---- Time / FPS ----====---- + { + if(fps > 0) { + stream << "FPS\t" << fps; + } + } + break; + + case KRRenderSettings::KRENGINE_DEBUG_DISPLAY_MEMORY: // ----====---- Memory Utilization ----=====---- + { +#if defined(__APPLE__) + // ---- CPU Memory ---- + + struct task_basic_info info; + mach_msg_type_number_t size = sizeof(info); + kern_return_t kerr = task_info(mach_task_self(), + TASK_BASIC_INFO, + (task_info_t)&info, + &size); + if( kerr == KERN_SUCCESS ) { + stream << "\tResident\tVirtual\tTotal"; + stream << "\nCPU\t" << (info.resident_size / 1024 / 1024) << " MB\t" << (info.virtual_size / 1024 / 1024) << " MB\t" << ((info.resident_size + info.virtual_size) / 1024 / 1024) << " MB"; + } else { + stream << "\nERROR: Could not get CPU memory utilization."; + } + + + mach_port_t host_port = mach_host_self(); + mach_msg_type_number_t host_size = sizeof(vm_statistics_data_t) / sizeof(integer_t); + vm_size_t pagesize = 0; + vm_statistics_data_t vm_stat; + if(host_page_size(host_port, &pagesize) != KERN_SUCCESS) { + stream << "\n\nERROR: Could not get VM page size."; + } else if(host_statistics(host_port, HOST_VM_INFO, (host_info_t)&vm_stat, &host_size) != KERN_SUCCESS) { + stream << "\n\nERROR: Could not get VM stats."; + } else { + stream << "\n\n\n\tWired\tActive\tInactive\tFree\tTotal"; + stream << "\nVM\t"; + stream << (vm_stat.wire_count * pagesize / 1024 / 1024) << " MB\t"; + stream << (vm_stat.active_count * pagesize / 1024 / 1024) << " MB\t"; + stream << (vm_stat.inactive_count * pagesize / 1024 / 1024) << " MB\t"; + stream << (vm_stat.free_count * pagesize / 1024 / 1024) << " MB\t"; + stream << ((vm_stat.wire_count + vm_stat.active_count + vm_stat.inactive_count) * pagesize / 1024 / 1024) << " MB"; + } +#endif // defined(__APPLE__) + + // ---- GPU Memory ---- + int texture_count_active = m_pContext->getTextureManager()->getActiveTextures().size(); + int texture_count = texture_count_active; + long texture_mem_active = m_pContext->getTextureManager()->getMemActive(); + long texture_mem_used = m_pContext->getTextureManager()->getMemUsed(); + long texture_mem_throughput = m_pContext->getTextureManager()->getMemoryTransferedThisFrame(); + + int vbo_count_active = m_pContext->getMeshManager()->getActiveVBOCount(); + long vbo_mem_active = m_pContext->getMeshManager()->getMemActive(); + long vbo_mem_used = m_pContext->getMeshManager()->getMemUsed(); + long vbo_mem_throughput = m_pContext->getMeshManager()->getMemoryTransferedThisFrame(); + + long total_mem_active = texture_mem_active + vbo_mem_active; + long total_mem_used = texture_mem_used + vbo_mem_used; + long total_mem_throughput = texture_mem_throughput + vbo_mem_throughput; + + stream << "\n\n\n\t# Active\t# Used\tActive\tUsed\tThroughput\n"; + + stream << "Textures\t" << texture_count_active << "\t" << texture_count << "\t" << (texture_mem_active / 1024) << " KB\t" << (texture_mem_used / 1024) << " KB\t" << (texture_mem_throughput / 1024) << " KB / frame\n"; + stream << "VBO's\t" << vbo_count_active << "\t" << vbo_count_active << "\t" << (vbo_mem_active / 1024) <<" KB\t" << (vbo_mem_used / 1024) << " KB\t" << (vbo_mem_throughput / 1024) << " KB / frame\n"; + stream << "\nGPU Total\t\t\t" << (total_mem_active / 1024) << " KB\t" << (total_mem_used / 1024) << " KB\t" << (total_mem_throughput / 1024) << " KB / frame"; + } + break; + + case KRRenderSettings::KRENGINE_DEBUG_DISPLAY_TEXTURES: // ----====---- List Active Textures ----====---- + { + bool first = true; + int texture_count = 0; + std::set active_textures = m_pContext->getTextureManager()->getActiveTextures(); + for(std::set::iterator itr=active_textures.begin(); itr != active_textures.end(); itr++) { + KRTexture *texture = *itr; + if(first) { + first = false; + } else { + stream << "\n"; + } + stream << texture->getName(); + stream << "\t"; + stream << texture->getMemSize() / 1024; + stream << " KB"; + stream << "\t"; + stream << texture->getMaxMipMap(); + if(texture->hasMipmaps() && texture->getCurrentLodMaxDim() != texture->getMaxMipMap()) { + stream << " px => "; + stream << texture->getCurrentLodMaxDim(); + } + stream << " px"; + texture_count++; + } + + stream << "\n\nTOTAL: "; + stream << texture_count; + stream << " textures\t"; + stream << (m_pContext->getTextureManager()->getMemActive() / 1024) << " KB"; + } + break; + + case KRRenderSettings::KRENGINE_DEBUG_DISPLAY_DRAW_CALLS: // ----====---- List Draw Calls ----====---- + { + std::vector draw_calls = m_pContext->getMeshManager()->getDrawCalls(); + + long draw_call_count = 0; + long vertex_count = 0; + stream << "\tVerts\tPass\tObject\tMaterial"; + for(std::vector::iterator itr = draw_calls.begin(); itr != draw_calls.end(); itr++) { + draw_call_count++; + stream << "\n" << draw_call_count << "\t" << (*itr).vertex_count << "\t"; + switch((*itr).pass) { + case KRNode::RENDER_PASS_FORWARD_OPAQUE: + stream << "opaq"; + break; + case KRNode::RENDER_PASS_DEFERRED_GBUFFER: + stream << "d gb"; + break; + case KRNode::RENDER_PASS_DEFERRED_LIGHTS: + stream << "d light"; + break; + case KRNode::RENDER_PASS_DEFERRED_OPAQUE: + stream << "d opaq"; + break; + case KRNode::RENDER_PASS_FORWARD_TRANSPARENT: + stream << "trans"; + break; + case KRNode::RENDER_PASS_PARTICLE_OCCLUSION: + stream << "p occl"; + break; + case KRNode::RENDER_PASS_ADDITIVE_PARTICLES: + stream << "a part"; + break; + case KRNode::RENDER_PASS_VOLUMETRIC_EFFECTS_ADDITIVE: + stream << "vol add"; + break; + case KRNode::RENDER_PASS_GENERATE_SHADOWMAPS: + stream << "g shadow"; + break; + case KRNode::RENDER_PASS_SHADOWMAP: + stream << "shadow"; + break; + default: + // Suppress warnings + break; + } + stream << "\t" << (*itr).object_name << "\t" << (*itr).material_name; + vertex_count += (*itr).vertex_count; + } + stream << "\n\n\t\tTOTAL:\t" << draw_call_count << " draw calls\t" << vertex_count << " vertices"; + } + break; + case KRRenderSettings::KRENGINE_DEBUG_DISPLAY_OCTREE: + stream << "Octree Visualization"; + break; + case KRRenderSettings::KRENGINE_DEBUG_DISPLAY_COLLIDERS: + stream << "Collider Visualization"; + break; + case KRRenderSettings::KRENGINE_DEBUG_DISPLAY_BONES: + stream << "Bone Visualization"; + case KRRenderSettings::KRENGINE_DEBUG_DISPLAY_SIREN_REVERB_ZONES: + stream << "Siren - Reverb Zones"; + break; + case KRRenderSettings::KRENGINE_DEBUG_DISPLAY_SIREN_AMBIENT_ZONES: + stream << "Siren - Ambient Zones"; + break; + case KRRenderSettings::KRENGINE_DEBUG_DISPLAY_NUMBER: + // Suppress warning + break; + } + return stream.str(); +} + + +const KRViewport &KRCamera::getViewport() const +{ + return m_viewport; +} + + +Vector2 KRCamera::getDownsample() +{ + return m_downsample; +} + +void KRCamera::setDownsample(float v) +{ + m_downsample = Vector2::Create(v); +} + +void KRCamera::setFadeColor(const Vector4 &fade_color) +{ + m_fade_color = fade_color; +} + +Vector4 KRCamera::getFadeColor() +{ + return m_fade_color; +} diff --git a/kraken/KRCollider.cpp b/kraken/KRCollider.cpp index bebcd70..5306a78 100755 --- a/kraken/KRCollider.cpp +++ b/kraken/KRCollider.cpp @@ -1,229 +1,229 @@ -// -// KRCollider.cpp -// KREngine -// -// Copyright 2012 Kearwood Gilbert. All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, are -// permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of -// conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list -// of conditions and the following disclaimer in the documentation and/or other materials -// provided with the distribution. -// -// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED -// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR -// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// The views and conclusions contained in the software and documentation are those of the -// authors and should not be interpreted as representing official policies, either expressed -// or implied, of Kearwood Gilbert. -// - -#include "KREngine-common.h" -#include "KRCollider.h" -#include "KRContext.h" -#include "KRMesh.h" - - -KRCollider::KRCollider(KRScene &scene, std::string collider_name, std::string model_name, unsigned int layer_mask, float audio_occlusion) : KRNode(scene, collider_name) { - m_model_name = model_name; - m_layer_mask = layer_mask; - m_audio_occlusion = audio_occlusion; -} - -KRCollider::~KRCollider() { - -} - -std::string KRCollider::getElementName() { - return "collider"; -} - -tinyxml2::XMLElement *KRCollider::saveXML( tinyxml2::XMLNode *parent) -{ - tinyxml2::XMLElement *e = KRNode::saveXML(parent); - e->SetAttribute("mesh", m_model_name.c_str()); - e->SetAttribute("layer_mask", m_layer_mask); - e->SetAttribute("audio_occlusion", m_audio_occlusion); - return e; -} - -void KRCollider::loadXML(tinyxml2::XMLElement *e) { - KRNode::loadXML(e); - - m_model_name = e->Attribute("mesh"); - - m_layer_mask = 65535; - if(e->QueryUnsignedAttribute("layer_mask", &m_layer_mask) != tinyxml2::XML_SUCCESS) { - m_layer_mask = 65535; - } - - m_audio_occlusion = 1.0f; - if(e->QueryFloatAttribute("audio_occlusion", &m_audio_occlusion) != tinyxml2::XML_SUCCESS) { - m_audio_occlusion = 1.0f; - } -} - -void KRCollider::loadModel() { - if(m_models.size() == 0) { - m_models = m_pContext->getMeshManager()->getModel(m_model_name.c_str()); // The model manager returns the LOD levels in sorted order, with the highest detail first - if(m_models.size() > 0) { - getScene().notify_sceneGraphModify(this); - } - } -} - -AABB KRCollider::getBounds() { - loadModel(); - if(m_models.size() > 0) { - return AABB(m_models[0]->getMinPoint(), m_models[0]->getMaxPoint(), getModelMatrix()); - } else { - return AABB::Infinite(); - } -} - -bool KRCollider::lineCast(const Vector3 &v0, const Vector3 &v1, HitInfo &hitinfo, unsigned int layer_mask) -{ - if(layer_mask & m_layer_mask ) { // Only test if layer masks have a common bit set - loadModel(); - if(m_models.size()) { - if(getBounds().intersectsLine(v0, v1)) { - Vector3 v0_model_space = Matrix4::Dot(getInverseModelMatrix(), v0); - Vector3 v1_model_space = Matrix4::Dot(getInverseModelMatrix(), v1); - HitInfo hitinfo_model_space; - if(hitinfo.didHit()) { - Vector3 hit_position_model_space = Matrix4::Dot(getInverseModelMatrix(), hitinfo.getPosition()); - hitinfo_model_space = HitInfo(hit_position_model_space, Matrix4::DotNoTranslate(getInverseModelMatrix(), hitinfo.getNormal()), (hit_position_model_space - v0_model_space).magnitude(), hitinfo.getNode()); - } - - if(m_models[0]->lineCast(v0_model_space, v1_model_space, hitinfo_model_space)) { - Vector3 hit_position_world_space = Matrix4::Dot(getModelMatrix(), hitinfo_model_space.getPosition()); - hitinfo = HitInfo(hit_position_world_space, Vector3::Normalize(Matrix4::DotNoTranslate(getModelMatrix(), hitinfo_model_space.getNormal())), (hit_position_world_space - v0).magnitude(), this); - return true; - } - } - } - } - return false; -} - -bool KRCollider::rayCast(const Vector3 &v0, const Vector3 &dir, HitInfo &hitinfo, unsigned int layer_mask) -{ - if(layer_mask & m_layer_mask) { // Only test if layer masks have a common bit set - loadModel(); - if(m_models.size()) { - if(getBounds().intersectsRay(v0, dir)) { - Vector3 v0_model_space = Matrix4::Dot(getInverseModelMatrix(), v0); - Vector3 dir_model_space = Vector3::Normalize(Matrix4::DotNoTranslate(getInverseModelMatrix(), dir)); - HitInfo hitinfo_model_space; - if(hitinfo.didHit()) { - Vector3 hit_position_model_space = Matrix4::Dot(getInverseModelMatrix(), hitinfo.getPosition()); - hitinfo_model_space = HitInfo(hit_position_model_space, Vector3::Normalize(Matrix4::DotNoTranslate(getInverseModelMatrix(), hitinfo.getNormal())), (hit_position_model_space - v0_model_space).magnitude(), hitinfo.getNode()); - } - - if(m_models[0]->rayCast(v0_model_space, dir_model_space, hitinfo_model_space)) { - Vector3 hit_position_world_space = Matrix4::Dot(getModelMatrix(), hitinfo_model_space.getPosition()); - hitinfo = HitInfo(hit_position_world_space, Vector3::Normalize(Matrix4::DotNoTranslate(getModelMatrix(), hitinfo_model_space.getNormal())), (hit_position_world_space - v0).magnitude(), this); - return true; - } - } - } - } - return false; -} - -bool KRCollider::sphereCast(const Vector3 &v0, const Vector3 &v1, float radius, HitInfo &hitinfo, unsigned int layer_mask) -{ - if(layer_mask & m_layer_mask) { // Only test if layer masks have a common bit set - loadModel(); - if(m_models.size()) { - AABB sphereCastBounds = AABB( // TODO - Need to cache this; perhaps encasulate within a "spherecast" class to be passed through these functions - Vector3(KRMIN(v0.x, v1.x) - radius, KRMIN(v0.y, v1.y) - radius, KRMIN(v0.z, v1.z) - radius), - Vector3(KRMAX(v0.x, v1.x) + radius, KRMAX(v0.y, v1.y) + radius, KRMAX(v0.z, v1.z) + radius) - ); - - if(getBounds().intersects(sphereCastBounds)) { - if(m_models[0]->sphereCast(getModelMatrix(), v0, v1, radius, hitinfo)) { - hitinfo = HitInfo(hitinfo.getPosition(), hitinfo.getNormal(), hitinfo.getDistance(), this); - return true; - } - } - } - } - return false; -} - -unsigned int KRCollider::getLayerMask() -{ - return m_layer_mask; -} - -void KRCollider::setLayerMask(unsigned int layer_mask) -{ - m_layer_mask = layer_mask; -} - -float KRCollider::getAudioOcclusion() -{ - return m_audio_occlusion; -} - -void KRCollider::setAudioOcclusion(float audio_occlusion) -{ - m_audio_occlusion = audio_occlusion; -} - - -void KRCollider::render(KRCamera *pCamera, std::vector &point_lights, std::vector &directional_lights, std::vector&spot_lights, const KRViewport &viewport, KRNode::RenderPass renderPass) -{ - if(m_lod_visible <= LOD_VISIBILITY_PRESTREAM) return; - - KRNode::render(pCamera, point_lights, directional_lights, spot_lights, viewport, renderPass); - - if(renderPass == KRNode::RENDER_PASS_FORWARD_TRANSPARENT && pCamera->settings.debug_display == KRRenderSettings::KRENGINE_DEBUG_DISPLAY_COLLIDERS) { - loadModel(); - if(m_models.size()) { - - GL_PUSH_GROUP_MARKER("Debug Overlays"); - - KRShader *pShader = getContext().getShaderManager()->getShader("visualize_overlay", pCamera, point_lights, directional_lights, spot_lights, 0, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, renderPass); - - if(getContext().getShaderManager()->selectShader(*pCamera, pShader, viewport, getModelMatrix(), point_lights, directional_lights, spot_lights, 0, renderPass, Vector3::Zero(), 0.0f, Vector4::Zero())) { - - // Enable additive blending - GLDEBUG(glEnable(GL_BLEND)); - GLDEBUG(glBlendFunc(GL_ONE, GL_ONE)); - - - // Disable z-buffer write - GLDEBUG(glDepthMask(GL_FALSE)); - - // Enable z-buffer test - GLDEBUG(glEnable(GL_DEPTH_TEST)); - GLDEBUG(glDepthFunc(GL_LEQUAL)); - GLDEBUG(glDepthRangef(0.0, 1.0)); - - - for(int i=0; i < m_models[0]->getSubmeshCount(); i++) { - m_models[0]->renderSubmesh(i, renderPass, getName(), "visualize_overlay", 1.0f); - } - - // Enable alpha blending - GLDEBUG(glEnable(GL_BLEND)); - GLDEBUG(glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA)); - } - - GL_POP_GROUP_MARKER; - } - } -} +// +// KRCollider.cpp +// KREngine +// +// Copyright 2012 Kearwood Gilbert. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other materials +// provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// The views and conclusions contained in the software and documentation are those of the +// authors and should not be interpreted as representing official policies, either expressed +// or implied, of Kearwood Gilbert. +// + +#include "KREngine-common.h" +#include "KRCollider.h" +#include "KRContext.h" +#include "KRMesh.h" + + +KRCollider::KRCollider(KRScene &scene, std::string collider_name, std::string model_name, unsigned int layer_mask, float audio_occlusion) : KRNode(scene, collider_name) { + m_model_name = model_name; + m_layer_mask = layer_mask; + m_audio_occlusion = audio_occlusion; +} + +KRCollider::~KRCollider() { + +} + +std::string KRCollider::getElementName() { + return "collider"; +} + +tinyxml2::XMLElement *KRCollider::saveXML( tinyxml2::XMLNode *parent) +{ + tinyxml2::XMLElement *e = KRNode::saveXML(parent); + e->SetAttribute("mesh", m_model_name.c_str()); + e->SetAttribute("layer_mask", m_layer_mask); + e->SetAttribute("audio_occlusion", m_audio_occlusion); + return e; +} + +void KRCollider::loadXML(tinyxml2::XMLElement *e) { + KRNode::loadXML(e); + + m_model_name = e->Attribute("mesh"); + + m_layer_mask = 65535; + if(e->QueryUnsignedAttribute("layer_mask", &m_layer_mask) != tinyxml2::XML_SUCCESS) { + m_layer_mask = 65535; + } + + m_audio_occlusion = 1.0f; + if(e->QueryFloatAttribute("audio_occlusion", &m_audio_occlusion) != tinyxml2::XML_SUCCESS) { + m_audio_occlusion = 1.0f; + } +} + +void KRCollider::loadModel() { + if(m_models.size() == 0) { + m_models = m_pContext->getMeshManager()->getModel(m_model_name.c_str()); // The model manager returns the LOD levels in sorted order, with the highest detail first + if(m_models.size() > 0) { + getScene().notify_sceneGraphModify(this); + } + } +} + +AABB KRCollider::getBounds() { + loadModel(); + if(m_models.size() > 0) { + return AABB::Create(m_models[0]->getMinPoint(), m_models[0]->getMaxPoint(), getModelMatrix()); + } else { + return AABB::Infinite(); + } +} + +bool KRCollider::lineCast(const Vector3 &v0, const Vector3 &v1, HitInfo &hitinfo, unsigned int layer_mask) +{ + if(layer_mask & m_layer_mask ) { // Only test if layer masks have a common bit set + loadModel(); + if(m_models.size()) { + if(getBounds().intersectsLine(v0, v1)) { + Vector3 v0_model_space = Matrix4::Dot(getInverseModelMatrix(), v0); + Vector3 v1_model_space = Matrix4::Dot(getInverseModelMatrix(), v1); + HitInfo hitinfo_model_space; + if(hitinfo.didHit()) { + Vector3 hit_position_model_space = Matrix4::Dot(getInverseModelMatrix(), hitinfo.getPosition()); + hitinfo_model_space = HitInfo(hit_position_model_space, Matrix4::DotNoTranslate(getInverseModelMatrix(), hitinfo.getNormal()), (hit_position_model_space - v0_model_space).magnitude(), hitinfo.getNode()); + } + + if(m_models[0]->lineCast(v0_model_space, v1_model_space, hitinfo_model_space)) { + Vector3 hit_position_world_space = Matrix4::Dot(getModelMatrix(), hitinfo_model_space.getPosition()); + hitinfo = HitInfo(hit_position_world_space, Vector3::Normalize(Matrix4::DotNoTranslate(getModelMatrix(), hitinfo_model_space.getNormal())), (hit_position_world_space - v0).magnitude(), this); + return true; + } + } + } + } + return false; +} + +bool KRCollider::rayCast(const Vector3 &v0, const Vector3 &dir, HitInfo &hitinfo, unsigned int layer_mask) +{ + if(layer_mask & m_layer_mask) { // Only test if layer masks have a common bit set + loadModel(); + if(m_models.size()) { + if(getBounds().intersectsRay(v0, dir)) { + Vector3 v0_model_space = Matrix4::Dot(getInverseModelMatrix(), v0); + Vector3 dir_model_space = Vector3::Normalize(Matrix4::DotNoTranslate(getInverseModelMatrix(), dir)); + HitInfo hitinfo_model_space; + if(hitinfo.didHit()) { + Vector3 hit_position_model_space = Matrix4::Dot(getInverseModelMatrix(), hitinfo.getPosition()); + hitinfo_model_space = HitInfo(hit_position_model_space, Vector3::Normalize(Matrix4::DotNoTranslate(getInverseModelMatrix(), hitinfo.getNormal())), (hit_position_model_space - v0_model_space).magnitude(), hitinfo.getNode()); + } + + if(m_models[0]->rayCast(v0_model_space, dir_model_space, hitinfo_model_space)) { + Vector3 hit_position_world_space = Matrix4::Dot(getModelMatrix(), hitinfo_model_space.getPosition()); + hitinfo = HitInfo(hit_position_world_space, Vector3::Normalize(Matrix4::DotNoTranslate(getModelMatrix(), hitinfo_model_space.getNormal())), (hit_position_world_space - v0).magnitude(), this); + return true; + } + } + } + } + return false; +} + +bool KRCollider::sphereCast(const Vector3 &v0, const Vector3 &v1, float radius, HitInfo &hitinfo, unsigned int layer_mask) +{ + if(layer_mask & m_layer_mask) { // Only test if layer masks have a common bit set + loadModel(); + if(m_models.size()) { + AABB sphereCastBounds = AABB::Create( // TODO - Need to cache this; perhaps encasulate within a "spherecast" class to be passed through these functions + Vector3::Create(KRMIN(v0.x, v1.x) - radius, KRMIN(v0.y, v1.y) - radius, KRMIN(v0.z, v1.z) - radius), + Vector3::Create(KRMAX(v0.x, v1.x) + radius, KRMAX(v0.y, v1.y) + radius, KRMAX(v0.z, v1.z) + radius) + ); + + if(getBounds().intersects(sphereCastBounds)) { + if(m_models[0]->sphereCast(getModelMatrix(), v0, v1, radius, hitinfo)) { + hitinfo = HitInfo(hitinfo.getPosition(), hitinfo.getNormal(), hitinfo.getDistance(), this); + return true; + } + } + } + } + return false; +} + +unsigned int KRCollider::getLayerMask() +{ + return m_layer_mask; +} + +void KRCollider::setLayerMask(unsigned int layer_mask) +{ + m_layer_mask = layer_mask; +} + +float KRCollider::getAudioOcclusion() +{ + return m_audio_occlusion; +} + +void KRCollider::setAudioOcclusion(float audio_occlusion) +{ + m_audio_occlusion = audio_occlusion; +} + + +void KRCollider::render(KRCamera *pCamera, std::vector &point_lights, std::vector &directional_lights, std::vector&spot_lights, const KRViewport &viewport, KRNode::RenderPass renderPass) +{ + if(m_lod_visible <= LOD_VISIBILITY_PRESTREAM) return; + + KRNode::render(pCamera, point_lights, directional_lights, spot_lights, viewport, renderPass); + + if(renderPass == KRNode::RENDER_PASS_FORWARD_TRANSPARENT && pCamera->settings.debug_display == KRRenderSettings::KRENGINE_DEBUG_DISPLAY_COLLIDERS) { + loadModel(); + if(m_models.size()) { + + GL_PUSH_GROUP_MARKER("Debug Overlays"); + + KRShader *pShader = getContext().getShaderManager()->getShader("visualize_overlay", pCamera, point_lights, directional_lights, spot_lights, 0, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, renderPass); + + if(getContext().getShaderManager()->selectShader(*pCamera, pShader, viewport, getModelMatrix(), point_lights, directional_lights, spot_lights, 0, renderPass, Vector3::Zero(), 0.0f, Vector4::Zero())) { + + // Enable additive blending + GLDEBUG(glEnable(GL_BLEND)); + GLDEBUG(glBlendFunc(GL_ONE, GL_ONE)); + + + // Disable z-buffer write + GLDEBUG(glDepthMask(GL_FALSE)); + + // Enable z-buffer test + GLDEBUG(glEnable(GL_DEPTH_TEST)); + GLDEBUG(glDepthFunc(GL_LEQUAL)); + GLDEBUG(glDepthRangef(0.0, 1.0)); + + + for(int i=0; i < m_models[0]->getSubmeshCount(); i++) { + m_models[0]->renderSubmesh(i, renderPass, getName(), "visualize_overlay", 1.0f); + } + + // Enable alpha blending + GLDEBUG(glEnable(GL_BLEND)); + GLDEBUG(glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA)); + } + + GL_POP_GROUP_MARKER; + } + } +} diff --git a/kraken/KRDSP_slow.cpp b/kraken/KRDSP_slow.cpp index 55056ad..429219c 100644 --- a/kraken/KRDSP_slow.cpp +++ b/kraken/KRDSP_slow.cpp @@ -1,207 +1,207 @@ -// -// KREngine.h -// KREngine -// -// Copyright 2012 Kearwood Gilbert. All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, are -// permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of -// conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list -// of conditions and the following disclaimer in the documentation and/or other materials -// provided with the distribution. -// -// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED -// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR -// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// The views and conclusions contained in the software and documentation are those of the -// authors and should not be interpreted as representing official policies, either expressed -// or implied, of Kearwood Gilbert. -// - -#include "KRDSP.h" - -#ifdef KRDSP_SLOW - -#include "KREngine-common.h" - -namespace KRDSP { - -FFTWorkspace::FFTWorkspace() -{ - sin_table = nullptr; - cos_table = nullptr; -} - -FFTWorkspace::~FFTWorkspace() -{ - destroy(); -} - -void FFTWorkspace::create(size_t length) -{ - size_t size = (length / 2); - cos_table = new float[size]; - sin_table = new float[size]; - for (int i = 0; i < size / 2; i++) { - float a = 2 * M_PI * i / length; - cos_table[i] = cos(a); - sin_table[i] = sin(a); - } -} - -void FFTWorkspace::destroy() -{ - if (sin_table) { - delete sin_table; - sin_table = nullptr; - } - if (cos_table) { - delete cos_table; - cos_table = nullptr; - } -} - -void FFTForward(const FFTWorkspace &workspace, SplitComplex *src, size_t count) -{ - // Radix-2 Decimation in Time FFT Algorithm - // http://en.dsplib.org/content/fft_dec_in_time.html - - // Only power-of-two sizes supported - assert((count & (count - 1)) == 0); - - int levels = 0; - while (1 << levels <= count) { - levels++; - } - - for (size_t i = 0; i < count; i++) { - size_t j = 0; - for (int k = 0; k < levels; k++) { - j <<= 1; - j |= ((i >> k) & 1); - } - if (j > i) { - float temp = src->realp[i]; - src->realp[i] = src->realp[j]; - src->realp[j] = temp; - temp = src->imagp[i]; - src->imagp[i] = src->imagp[j]; - src->imagp[j] = temp; - } - } - - for (size_t size = 2; size <= count; size *= 2) { - size_t halfsize = size / 2; - size_t step = count / size; - for (size_t i = 0; i < count; i += size) { - for (size_t j = i, k = 0; j < i + halfsize; j++, k += step) { - float temp_real = src->realp[j + halfsize] * workspace.cos_table[k]; - temp_real += src->imagp[j + halfsize] * workspace.sin_table[k]; - float temp_imag = -src->realp[j + halfsize] * workspace.sin_table[k]; - temp_imag += src->imagp[j + halfsize] * workspace.cos_table[k]; - src->realp[j + halfsize] = src->realp[j] - temp_real; - src->imagp[j + halfsize] = src->imagp[j] - temp_imag; - src->realp[j] += temp_real; - src->imagp[j] += temp_imag; - } - } - } -} - -void FFTInverse(const FFTWorkspace &workspace, SplitComplex *src, size_t count) -{ - SplitComplex swapped; - swapped.imagp = src->realp; - swapped.realp = src->imagp; - FFTForward(workspace, &swapped, count); -} - -void Int16ToFloat(const short *src, size_t srcStride, float *dest, size_t destStride, size_t count) -{ - const short *r = src; - float *w = dest; - while (w < dest + destStride * count) { - *w = (float)*r; - r += srcStride; - w += destStride; - } -} - -void Scale(float *buffer, float scale, size_t count) -{ - float *w = buffer; - while (w < buffer + count) { - *w *= scale; - w++; - } -} - -void ScaleCopy(const float *src, float scale, float *dest, size_t count) -{ - const float *r = src; - float *w = dest; - while (w < dest + count) { - *w = *r * scale; - w++; - r++; - } -} - -void ScaleCopy(const SplitComplex *src, float scale, SplitComplex *dest, size_t count) -{ - ScaleCopy(src->realp, scale, dest->realp, count); - ScaleCopy(src->imagp, scale, dest->imagp, count); -} - -void ScaleRamp(float *buffer, float scaleStart, float scaleStep, size_t count) -{ - float *w = buffer; - float s = scaleStart; - while (w < buffer + count) { - *w *= s; - w++; - s += scaleStep; - } -} - -void Accumulate(float *buffer, size_t bufferStride, const float *buffer2, size_t buffer2Stride, size_t count) -{ - float *w = buffer; - const float *r = buffer2; - while (w < buffer + bufferStride * count) { - *w *= *r; - w += bufferStride; - r += buffer2Stride; - } -} - -void Accumulate(SplitComplex *buffer, const SplitComplex *buffer2, size_t count) -{ - for (size_t i = 0; i < count; i++) { - buffer->imagp[i] += buffer2->imagp[i]; - buffer->realp[i] += buffer2->realp[i]; - } -} - -void Multiply(const SplitComplex *a, const SplitComplex *b, SplitComplex *c, size_t count) -{ - for (size_t i = 0; i < count; i++) { - c->realp[i] = a->realp[i] * b->realp[i] - a->imagp[i] * b->imagp[i]; - c->imagp[i] = a->realp[i] * b->imagp[i] + a->imagp[i] * b->realp[i]; - } -} - -} // namespace KRDSP - -#endif // KRDSP_SLOW +// +// KREngine.h +// KREngine +// +// Copyright 2012 Kearwood Gilbert. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other materials +// provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// The views and conclusions contained in the software and documentation are those of the +// authors and should not be interpreted as representing official policies, either expressed +// or implied, of Kearwood Gilbert. +// + +#include "KRDSP.h" + +#ifdef KRDSP_SLOW + +#include "KREngine-common.h" + +namespace KRDSP { + +FFTWorkspace::FFTWorkspace() +{ + sin_table = nullptr; + cos_table = nullptr; +} + +FFTWorkspace::~FFTWorkspace() +{ + destroy(); +} + +void FFTWorkspace::create(size_t length) +{ + size_t size = (length / 2); + cos_table = new float[size]; + sin_table = new float[size]; + for (int i = 0; i < size / 2; i++) { + float a = 2.0f * M_PI * i / length; + cos_table[i] = cos(a); + sin_table[i] = sin(a); + } +} + +void FFTWorkspace::destroy() +{ + if (sin_table) { + delete sin_table; + sin_table = nullptr; + } + if (cos_table) { + delete cos_table; + cos_table = nullptr; + } +} + +void FFTForward(const FFTWorkspace &workspace, SplitComplex *src, size_t count) +{ + // Radix-2 Decimation in Time FFT Algorithm + // http://en.dsplib.org/content/fft_dec_in_time.html + + // Only power-of-two sizes supported + assert((count & (count - 1)) == 0); + + int levels = 0; + while (1 << levels <= count) { + levels++; + } + + for (size_t i = 0; i < count; i++) { + size_t j = 0; + for (int k = 0; k < levels; k++) { + j <<= 1; + j |= ((i >> k) & 1); + } + if (j > i) { + float temp = src->realp[i]; + src->realp[i] = src->realp[j]; + src->realp[j] = temp; + temp = src->imagp[i]; + src->imagp[i] = src->imagp[j]; + src->imagp[j] = temp; + } + } + + for (size_t size = 2; size <= count; size *= 2) { + size_t halfsize = size / 2; + size_t step = count / size; + for (size_t i = 0; i < count; i += size) { + for (size_t j = i, k = 0; j < i + halfsize; j++, k += step) { + float temp_real = src->realp[j + halfsize] * workspace.cos_table[k]; + temp_real += src->imagp[j + halfsize] * workspace.sin_table[k]; + float temp_imag = -src->realp[j + halfsize] * workspace.sin_table[k]; + temp_imag += src->imagp[j + halfsize] * workspace.cos_table[k]; + src->realp[j + halfsize] = src->realp[j] - temp_real; + src->imagp[j + halfsize] = src->imagp[j] - temp_imag; + src->realp[j] += temp_real; + src->imagp[j] += temp_imag; + } + } + } +} + +void FFTInverse(const FFTWorkspace &workspace, SplitComplex *src, size_t count) +{ + SplitComplex swapped; + swapped.imagp = src->realp; + swapped.realp = src->imagp; + FFTForward(workspace, &swapped, count); +} + +void Int16ToFloat(const short *src, size_t srcStride, float *dest, size_t destStride, size_t count) +{ + const short *r = src; + float *w = dest; + while (w < dest + destStride * count) { + *w = (float)*r; + r += srcStride; + w += destStride; + } +} + +void Scale(float *buffer, float scale, size_t count) +{ + float *w = buffer; + while (w < buffer + count) { + *w *= scale; + w++; + } +} + +void ScaleCopy(const float *src, float scale, float *dest, size_t count) +{ + const float *r = src; + float *w = dest; + while (w < dest + count) { + *w = *r * scale; + w++; + r++; + } +} + +void ScaleCopy(const SplitComplex *src, float scale, SplitComplex *dest, size_t count) +{ + ScaleCopy(src->realp, scale, dest->realp, count); + ScaleCopy(src->imagp, scale, dest->imagp, count); +} + +void ScaleRamp(float *buffer, float scaleStart, float scaleStep, size_t count) +{ + float *w = buffer; + float s = scaleStart; + while (w < buffer + count) { + *w *= s; + w++; + s += scaleStep; + } +} + +void Accumulate(float *buffer, size_t bufferStride, const float *buffer2, size_t buffer2Stride, size_t count) +{ + float *w = buffer; + const float *r = buffer2; + while (w < buffer + bufferStride * count) { + *w *= *r; + w += bufferStride; + r += buffer2Stride; + } +} + +void Accumulate(SplitComplex *buffer, const SplitComplex *buffer2, size_t count) +{ + for (size_t i = 0; i < count; i++) { + buffer->imagp[i] += buffer2->imagp[i]; + buffer->realp[i] += buffer2->realp[i]; + } +} + +void Multiply(const SplitComplex *a, const SplitComplex *b, SplitComplex *c, size_t count) +{ + for (size_t i = 0; i < count; i++) { + c->realp[i] = a->realp[i] * b->realp[i] - a->imagp[i] * b->imagp[i]; + c->imagp[i] = a->realp[i] * b->imagp[i] + a->imagp[i] * b->realp[i]; + } +} + +} // namespace KRDSP + +#endif // KRDSP_SLOW diff --git a/kraken/KRDirectionalLight.cpp b/kraken/KRDirectionalLight.cpp index 6f47bd8..e8f703d 100755 --- a/kraken/KRDirectionalLight.cpp +++ b/kraken/KRDirectionalLight.cpp @@ -1,135 +1,135 @@ -// -// KRDirectionalLight.cpp -// KREngine -// -// Created by Kearwood Gilbert on 12-04-05. -// Copyright (c) 2012 Kearwood Software. All rights reserved. -// - -#include "KREngine-common.h" - -#include "KRDirectionalLight.h" -#include "KRShader.h" -#include "KRContext.h" -#include "assert.h" -#include "KRStockGeometry.h" - -KRDirectionalLight::KRDirectionalLight(KRScene &scene, std::string name) : KRLight(scene, name) -{ - -} - -KRDirectionalLight::~KRDirectionalLight() -{ - -} - -std::string KRDirectionalLight::getElementName() { - return "directional_light"; -} - -Vector3 KRDirectionalLight::getWorldLightDirection() { - return Matrix4::Dot(getWorldRotation().rotationMatrix(), getLocalLightDirection()); -} - -Vector3 KRDirectionalLight::getLocalLightDirection() { - return Vector3::Up(); //&KRF HACK changed from Vector3::Forward(); - to compensate for the way Maya handles post rotation. -} - - -int KRDirectionalLight::configureShadowBufferViewports(const KRViewport &viewport) { - - const float KRENGINE_SHADOW_BOUNDS_EXTRA_SCALE = 1.25f; // Scale to apply to view frustrum bounds so that we don't need to refresh shadows on every frame - int cShadows = 1; - for(int iShadow=0; iShadow < cShadows; iShadow++) { - /* - TODO - Determine if we still need this... - - GLfloat shadowMinDepths[3][3] = {{0.0f, 0.0f, 0.0f},{0.0f, 0.0f, 0.0f},{0.0f, 0.05f, 0.3f}}; - GLfloat shadowMaxDepths[3][3] = {{0.0f, 0.0f, 1.0f},{0.1f, 0.0f, 0.0f},{0.1f, 0.3f, 1.0f}}; - - float min_depth = 0.0f; - float max_depth = 1.0f; - */ - - AABB worldSpacefrustrumSliceBounds = AABB(Vector3(-1.0f, -1.0f, -1.0f), Vector3(1.0f, 1.0f, 1.0f), Matrix4::Invert(viewport.getViewProjectionMatrix())); - worldSpacefrustrumSliceBounds.scale(KRENGINE_SHADOW_BOUNDS_EXTRA_SCALE); - - Vector3 shadowLook = -Vector3::Normalize(getWorldLightDirection()); - - Vector3 shadowUp(0.0, 1.0, 0.0); - if(Vector3::Dot(shadowUp, shadowLook) > 0.99f) shadowUp = Vector3(0.0, 0.0, 1.0); // Ensure shadow look direction is not parallel with the shadowUp direction - -// Matrix4 matShadowView = Matrix4::LookAt(viewport.getCameraPosition() - shadowLook, viewport.getCameraPosition(), shadowUp); -// Matrix4 matShadowProjection = Matrix4(); -// matShadowProjection.scale(0.001, 0.001, 0.001); - - Matrix4 matShadowView = Matrix4::LookAt(worldSpacefrustrumSliceBounds.center() - shadowLook, worldSpacefrustrumSliceBounds.center(), shadowUp); - Matrix4 matShadowProjection = Matrix4(); - AABB shadowSpaceFrustrumSliceBounds = AABB(worldSpacefrustrumSliceBounds.min, worldSpacefrustrumSliceBounds.max, Matrix4::Invert(matShadowProjection)); - AABB shadowSpaceSceneBounds = AABB(getScene().getRootOctreeBounds().min, getScene().getRootOctreeBounds().max, Matrix4::Invert(matShadowProjection)); - if(shadowSpaceSceneBounds.min.z < shadowSpaceFrustrumSliceBounds.min.z) shadowSpaceFrustrumSliceBounds.min.z = shadowSpaceSceneBounds.min.z; // Include any potential shadow casters that are outside the view frustrum - matShadowProjection.scale(1.0f / shadowSpaceFrustrumSliceBounds.size().x, 1.0f / shadowSpaceFrustrumSliceBounds.size().y, 1.0f / shadowSpaceFrustrumSliceBounds.size().z); - - Matrix4 matBias; - matBias.bias(); - matShadowProjection *= matBias; - - KRViewport newShadowViewport = KRViewport(Vector2(KRENGINE_SHADOW_MAP_WIDTH, KRENGINE_SHADOW_MAP_HEIGHT), matShadowView, matShadowProjection); - AABB prevShadowBounds = AABB(-Vector3::One(), Vector3::One(), Matrix4::Invert(m_shadowViewports[iShadow].getViewProjectionMatrix())); - AABB minimumShadowBounds = AABB(-Vector3::One(), Vector3::One(), Matrix4::Invert(newShadowViewport.getViewProjectionMatrix())); - minimumShadowBounds.scale(1.0f / KRENGINE_SHADOW_BOUNDS_EXTRA_SCALE); - if(!prevShadowBounds.contains(minimumShadowBounds) || !shadowValid[iShadow] || true) { // FINDME, HACK - Re-generating the shadow map every frame. This should only be needed if the shadow contains non-static geometry - m_shadowViewports[iShadow] = newShadowViewport; - shadowValid[iShadow] = false; - fprintf(stderr, "Kraken - Generate shadow maps...\n"); - } - } - - return 1; -} - -void KRDirectionalLight::render(KRCamera *pCamera, std::vector &point_lights, std::vector &directional_lights, std::vector&spot_lights, const KRViewport &viewport, KRNode::RenderPass renderPass) { - - if(m_lod_visible <= LOD_VISIBILITY_PRESTREAM) return; - - KRLight::render(pCamera, point_lights, directional_lights, spot_lights, viewport, renderPass); - - if(renderPass == KRNode::RENDER_PASS_DEFERRED_LIGHTS) { - // Lights are rendered on the second pass of the deferred renderer - - std::vector this_light; - this_light.push_back(this); - - Matrix4 matModelViewInverseTranspose = viewport.getViewMatrix() * getModelMatrix(); - matModelViewInverseTranspose.transpose(); - matModelViewInverseTranspose.invert(); - - Vector3 light_direction_view_space = getWorldLightDirection(); - light_direction_view_space = Matrix4::Dot(matModelViewInverseTranspose, light_direction_view_space); - light_direction_view_space.normalize(); - - KRShader *pShader = getContext().getShaderManager()->getShader("light_directional", pCamera, std::vector(), this_light, std::vector(), 0, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, renderPass); - if(getContext().getShaderManager()->selectShader(*pCamera, pShader, viewport, getModelMatrix(), std::vector(), this_light, std::vector(), 0, renderPass, Vector3::Zero(), 0.0f, Vector4::Zero())) { - - pShader->setUniform(KRShader::KRENGINE_UNIFORM_LIGHT_DIRECTION_VIEW_SPACE, light_direction_view_space); - pShader->setUniform(KRShader::KRENGINE_UNIFORM_LIGHT_COLOR, m_color); - pShader->setUniform(KRShader::KRENGINE_UNIFORM_LIGHT_INTENSITY, m_intensity * 0.01f); - - // Disable z-buffer write - GLDEBUG(glDepthMask(GL_FALSE)); - - // Disable z-buffer test - GLDEBUG(glDisable(GL_DEPTH_TEST)); - - // Render a full screen quad - m_pContext->getMeshManager()->bindVBO(&getContext().getMeshManager()->KRENGINE_VBO_DATA_2D_SQUARE_VERTICES, 1.0f); - GLDEBUG(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)); - } - } -} - -AABB KRDirectionalLight::getBounds() -{ - return AABB::Infinite(); -} +// +// KRDirectionalLight.cpp +// KREngine +// +// Created by Kearwood Gilbert on 12-04-05. +// Copyright (c) 2012 Kearwood Software. All rights reserved. +// + +#include "KREngine-common.h" + +#include "KRDirectionalLight.h" +#include "KRShader.h" +#include "KRContext.h" +#include "assert.h" +#include "KRStockGeometry.h" + +KRDirectionalLight::KRDirectionalLight(KRScene &scene, std::string name) : KRLight(scene, name) +{ + +} + +KRDirectionalLight::~KRDirectionalLight() +{ + +} + +std::string KRDirectionalLight::getElementName() { + return "directional_light"; +} + +Vector3 KRDirectionalLight::getWorldLightDirection() { + return Matrix4::Dot(getWorldRotation().rotationMatrix(), getLocalLightDirection()); +} + +Vector3 KRDirectionalLight::getLocalLightDirection() { + return Vector3::Up(); //&KRF HACK changed from Vector3::Forward(); - to compensate for the way Maya handles post rotation. +} + + +int KRDirectionalLight::configureShadowBufferViewports(const KRViewport &viewport) { + + const float KRENGINE_SHADOW_BOUNDS_EXTRA_SCALE = 1.25f; // Scale to apply to view frustrum bounds so that we don't need to refresh shadows on every frame + int cShadows = 1; + for(int iShadow=0; iShadow < cShadows; iShadow++) { + /* + TODO - Determine if we still need this... + + GLfloat shadowMinDepths[3][3] = {{0.0f, 0.0f, 0.0f},{0.0f, 0.0f, 0.0f},{0.0f, 0.05f, 0.3f}}; + GLfloat shadowMaxDepths[3][3] = {{0.0f, 0.0f, 1.0f},{0.1f, 0.0f, 0.0f},{0.1f, 0.3f, 1.0f}}; + + float min_depth = 0.0f; + float max_depth = 1.0f; + */ + + AABB worldSpacefrustrumSliceBounds = AABB::Create(Vector3::Create(-1.0f, -1.0f, -1.0f), Vector3::Create(1.0f, 1.0f, 1.0f), Matrix4::Invert(viewport.getViewProjectionMatrix())); + worldSpacefrustrumSliceBounds.scale(KRENGINE_SHADOW_BOUNDS_EXTRA_SCALE); + + Vector3 shadowLook = -Vector3::Normalize(getWorldLightDirection()); + + Vector3 shadowUp = Vector3::Create(0.0, 1.0, 0.0); + if(Vector3::Dot(shadowUp, shadowLook) > 0.99f) shadowUp = Vector3::Create(0.0, 0.0, 1.0); // Ensure shadow look direction is not parallel with the shadowUp direction + +// Matrix4 matShadowView = Matrix4::LookAt(viewport.getCameraPosition() - shadowLook, viewport.getCameraPosition(), shadowUp); +// Matrix4 matShadowProjection = Matrix4(); +// matShadowProjection.scale(0.001, 0.001, 0.001); + + Matrix4 matShadowView = Matrix4::LookAt(worldSpacefrustrumSliceBounds.center() - shadowLook, worldSpacefrustrumSliceBounds.center(), shadowUp); + Matrix4 matShadowProjection = Matrix4(); + AABB shadowSpaceFrustrumSliceBounds = AABB::Create(worldSpacefrustrumSliceBounds.min, worldSpacefrustrumSliceBounds.max, Matrix4::Invert(matShadowProjection)); + AABB shadowSpaceSceneBounds = AABB::Create(getScene().getRootOctreeBounds().min, getScene().getRootOctreeBounds().max, Matrix4::Invert(matShadowProjection)); + if(shadowSpaceSceneBounds.min.z < shadowSpaceFrustrumSliceBounds.min.z) shadowSpaceFrustrumSliceBounds.min.z = shadowSpaceSceneBounds.min.z; // Include any potential shadow casters that are outside the view frustrum + matShadowProjection.scale(1.0f / shadowSpaceFrustrumSliceBounds.size().x, 1.0f / shadowSpaceFrustrumSliceBounds.size().y, 1.0f / shadowSpaceFrustrumSliceBounds.size().z); + + Matrix4 matBias; + matBias.bias(); + matShadowProjection *= matBias; + + KRViewport newShadowViewport = KRViewport(Vector2::Create(KRENGINE_SHADOW_MAP_WIDTH, KRENGINE_SHADOW_MAP_HEIGHT), matShadowView, matShadowProjection); + AABB prevShadowBounds = AABB::Create(-Vector3::One(), Vector3::One(), Matrix4::Invert(m_shadowViewports[iShadow].getViewProjectionMatrix())); + AABB minimumShadowBounds = AABB::Create(-Vector3::One(), Vector3::One(), Matrix4::Invert(newShadowViewport.getViewProjectionMatrix())); + minimumShadowBounds.scale(1.0f / KRENGINE_SHADOW_BOUNDS_EXTRA_SCALE); + if(!prevShadowBounds.contains(minimumShadowBounds) || !shadowValid[iShadow] || true) { // FINDME, HACK - Re-generating the shadow map every frame. This should only be needed if the shadow contains non-static geometry + m_shadowViewports[iShadow] = newShadowViewport; + shadowValid[iShadow] = false; + fprintf(stderr, "Kraken - Generate shadow maps...\n"); + } + } + + return 1; +} + +void KRDirectionalLight::render(KRCamera *pCamera, std::vector &point_lights, std::vector &directional_lights, std::vector&spot_lights, const KRViewport &viewport, KRNode::RenderPass renderPass) { + + if(m_lod_visible <= LOD_VISIBILITY_PRESTREAM) return; + + KRLight::render(pCamera, point_lights, directional_lights, spot_lights, viewport, renderPass); + + if(renderPass == KRNode::RENDER_PASS_DEFERRED_LIGHTS) { + // Lights are rendered on the second pass of the deferred renderer + + std::vector this_light; + this_light.push_back(this); + + Matrix4 matModelViewInverseTranspose = viewport.getViewMatrix() * getModelMatrix(); + matModelViewInverseTranspose.transpose(); + matModelViewInverseTranspose.invert(); + + Vector3 light_direction_view_space = getWorldLightDirection(); + light_direction_view_space = Matrix4::Dot(matModelViewInverseTranspose, light_direction_view_space); + light_direction_view_space.normalize(); + + KRShader *pShader = getContext().getShaderManager()->getShader("light_directional", pCamera, std::vector(), this_light, std::vector(), 0, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, renderPass); + if(getContext().getShaderManager()->selectShader(*pCamera, pShader, viewport, getModelMatrix(), std::vector(), this_light, std::vector(), 0, renderPass, Vector3::Zero(), 0.0f, Vector4::Zero())) { + + pShader->setUniform(KRShader::KRENGINE_UNIFORM_LIGHT_DIRECTION_VIEW_SPACE, light_direction_view_space); + pShader->setUniform(KRShader::KRENGINE_UNIFORM_LIGHT_COLOR, m_color); + pShader->setUniform(KRShader::KRENGINE_UNIFORM_LIGHT_INTENSITY, m_intensity * 0.01f); + + // Disable z-buffer write + GLDEBUG(glDepthMask(GL_FALSE)); + + // Disable z-buffer test + GLDEBUG(glDisable(GL_DEPTH_TEST)); + + // Render a full screen quad + m_pContext->getMeshManager()->bindVBO(&getContext().getMeshManager()->KRENGINE_VBO_DATA_2D_SQUARE_VERTICES, 1.0f); + GLDEBUG(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)); + } + } +} + +AABB KRDirectionalLight::getBounds() +{ + return AABB::Infinite(); +} diff --git a/kraken/KREngine-common.h b/kraken/KREngine-common.h index 07202d3..5e6917c 100755 --- a/kraken/KREngine-common.h +++ b/kraken/KREngine-common.h @@ -15,6 +15,8 @@ #include "KRHelpers.h" using namespace kraken; +#include "hydra.h" + #include #include #include diff --git a/kraken/KRHelpers.h b/kraken/KRHelpers.h index 247473d..c99da3b 100644 --- a/kraken/KRHelpers.h +++ b/kraken/KRHelpers.h @@ -1,6 +1,10 @@ #ifndef KRHELPERS_H #define KRHELPERS_H +#include "vector2.h" +#include "vector3.h" +#include "matrix4.h" + #if defined(_WIN32) || defined(_WIN64) #include #elif defined(__linux__) || defined(__unix__) || defined(__posix__) diff --git a/kraken/KRLODGroup.cpp b/kraken/KRLODGroup.cpp index f00376b..e81ee16 100755 --- a/kraken/KRLODGroup.cpp +++ b/kraken/KRLODGroup.cpp @@ -1,173 +1,173 @@ -// -// KRLODGroup.cpp -// KREngine -// -// Created by Kearwood Gilbert on 2012-12-06. -// Copyright (c) 2012 Kearwood Software. All rights reserved. -// - -#include "KRLODGroup.h" -#include "KRLODSet.h" -#include "KRContext.h" - -KRLODGroup::KRLODGroup(KRScene &scene, std::string name) : KRNode(scene, name) -{ - m_min_distance = 0.0f; - m_max_distance = 0.0f; - m_reference = AABB(Vector3::Zero(), Vector3::Zero()); - m_use_world_units = true; -} - -KRLODGroup::~KRLODGroup() -{ -} - -std::string KRLODGroup::getElementName() { - return "lod_group"; -} - -tinyxml2::XMLElement *KRLODGroup::saveXML( tinyxml2::XMLNode *parent) -{ - tinyxml2::XMLElement *e = KRNode::saveXML(parent); - e->SetAttribute("min_distance", m_min_distance); - e->SetAttribute("max_distance", m_max_distance); - - e->SetAttribute("reference_min_x", m_reference.min.x); - e->SetAttribute("reference_min_y", m_reference.min.y); - e->SetAttribute("reference_min_z", m_reference.min.z); - - - e->SetAttribute("reference_max_x", m_reference.max.x); - e->SetAttribute("reference_max_y", m_reference.max.y); - e->SetAttribute("reference_max_z", m_reference.max.z); - - e->SetAttribute("use_world_units", m_use_world_units ? "true" : "false"); - return e; -} - -void KRLODGroup::loadXML(tinyxml2::XMLElement *e) -{ - KRNode::loadXML(e); - - m_min_distance = 0.0f; - if(e->QueryFloatAttribute("min_distance", &m_min_distance) != tinyxml2::XML_SUCCESS) { - m_min_distance = 0.0f; - } - - m_max_distance = 0.0f; - if(e->QueryFloatAttribute("max_distance", &m_max_distance) != tinyxml2::XML_SUCCESS) { - m_max_distance = 0.0f; - } - - - float x=0.0f, y=0.0f, z=0.0f; - if(e->QueryFloatAttribute("reference_min_x", &x) != tinyxml2::XML_SUCCESS) { - x = 0.0f; - } - if(e->QueryFloatAttribute("reference_min_y", &y) != tinyxml2::XML_SUCCESS) { - y = 0.0f; - } - if(e->QueryFloatAttribute("reference_min_z", &z) != tinyxml2::XML_SUCCESS) { - z = 0.0f; - } - - m_reference.min = Vector3(x,y,z); - - x=0.0f; y=0.0f; z=0.0f; - if(e->QueryFloatAttribute("reference_max_x", &x) != tinyxml2::XML_SUCCESS) { - x = 0.0f; - } - if(e->QueryFloatAttribute("reference_max_y", &y) != tinyxml2::XML_SUCCESS) { - y = 0.0f; - } - if(e->QueryFloatAttribute("reference_max_z", &z) != tinyxml2::XML_SUCCESS) { - z = 0.0f; - } - m_reference.max = Vector3(x,y,z); - - m_use_world_units = true; - if(e->QueryBoolAttribute("use_world_units", &m_use_world_units) != tinyxml2::XML_SUCCESS) { - m_use_world_units = true; - } -} - - -const AABB &KRLODGroup::getReference() const -{ - return m_reference; -} - -void KRLODGroup::setReference(const AABB &reference) -{ - m_reference = reference; -} - -KRNode::LodVisibility KRLODGroup::calcLODVisibility(const KRViewport &viewport) -{ - if(m_min_distance == 0 && m_max_distance == 0) { - return LOD_VISIBILITY_VISIBLE; - } else { - float lod_bias = viewport.getLODBias(); - lod_bias = pow(2.0f, -lod_bias); - - // Compare using squared distances as sqrt is expensive - float sqr_distance; - float sqr_prestream_distance; - - Vector3 world_camera_position = viewport.getCameraPosition(); - Vector3 local_camera_position = worldToLocal(world_camera_position); - Vector3 local_reference_point = m_reference.nearestPoint(local_camera_position); - - if(m_use_world_units) { - Vector3 world_reference_point = localToWorld(local_reference_point); - sqr_distance = (world_camera_position - world_reference_point).sqrMagnitude() * (lod_bias * lod_bias); - sqr_prestream_distance = getContext().KRENGINE_PRESTREAM_DISTANCE * getContext().KRENGINE_PRESTREAM_DISTANCE; - } else { - sqr_distance = (local_camera_position - local_reference_point).sqrMagnitude() * (lod_bias * lod_bias); - - Vector3 world_reference_point = localToWorld(local_reference_point); - sqr_prestream_distance = worldToLocal(Vector3::Normalize(world_reference_point - world_camera_position) * getContext().KRENGINE_PRESTREAM_DISTANCE).sqrMagnitude(); // TODO, FINDME - Optimize with precalc? - - } - - float sqr_min_visible_distance = m_min_distance * m_min_distance; - float sqr_max_visible_distance = m_max_distance * m_max_distance; - if((sqr_distance >= sqr_min_visible_distance || m_min_distance == 0) && (sqr_distance < sqr_max_visible_distance || m_max_distance == 0)) { - return LOD_VISIBILITY_VISIBLE; - } else if((sqr_distance >= sqr_min_visible_distance - sqr_prestream_distance || m_min_distance == 0) && (sqr_distance < sqr_max_visible_distance + sqr_prestream_distance || m_max_distance == 0)) { - return LOD_VISIBILITY_PRESTREAM; - } else { - return LOD_VISIBILITY_HIDDEN; - } - } -} - -float KRLODGroup::getMinDistance() -{ - return m_min_distance; -} - -float KRLODGroup::getMaxDistance() -{ - return m_max_distance; -} - -void KRLODGroup::setMinDistance(float min_distance) -{ - m_min_distance = min_distance; -} - -void KRLODGroup::setMaxDistance(float max_distance) -{ - m_max_distance = max_distance; -} - -void KRLODGroup::setUseWorldUnits(bool use_world_units) -{ - m_use_world_units = use_world_units; -} - -bool KRLODGroup::getUseWorldUnits() const -{ - return m_use_world_units; -} +// +// KRLODGroup.cpp +// KREngine +// +// Created by Kearwood Gilbert on 2012-12-06. +// Copyright (c) 2012 Kearwood Software. All rights reserved. +// + +#include "KRLODGroup.h" +#include "KRLODSet.h" +#include "KRContext.h" + +KRLODGroup::KRLODGroup(KRScene &scene, std::string name) : KRNode(scene, name) +{ + m_min_distance = 0.0f; + m_max_distance = 0.0f; + m_reference = AABB::Create(Vector3::Zero(), Vector3::Zero()); + m_use_world_units = true; +} + +KRLODGroup::~KRLODGroup() +{ +} + +std::string KRLODGroup::getElementName() { + return "lod_group"; +} + +tinyxml2::XMLElement *KRLODGroup::saveXML( tinyxml2::XMLNode *parent) +{ + tinyxml2::XMLElement *e = KRNode::saveXML(parent); + e->SetAttribute("min_distance", m_min_distance); + e->SetAttribute("max_distance", m_max_distance); + + e->SetAttribute("reference_min_x", m_reference.min.x); + e->SetAttribute("reference_min_y", m_reference.min.y); + e->SetAttribute("reference_min_z", m_reference.min.z); + + + e->SetAttribute("reference_max_x", m_reference.max.x); + e->SetAttribute("reference_max_y", m_reference.max.y); + e->SetAttribute("reference_max_z", m_reference.max.z); + + e->SetAttribute("use_world_units", m_use_world_units ? "true" : "false"); + return e; +} + +void KRLODGroup::loadXML(tinyxml2::XMLElement *e) +{ + KRNode::loadXML(e); + + m_min_distance = 0.0f; + if(e->QueryFloatAttribute("min_distance", &m_min_distance) != tinyxml2::XML_SUCCESS) { + m_min_distance = 0.0f; + } + + m_max_distance = 0.0f; + if(e->QueryFloatAttribute("max_distance", &m_max_distance) != tinyxml2::XML_SUCCESS) { + m_max_distance = 0.0f; + } + + + float x=0.0f, y=0.0f, z=0.0f; + if(e->QueryFloatAttribute("reference_min_x", &x) != tinyxml2::XML_SUCCESS) { + x = 0.0f; + } + if(e->QueryFloatAttribute("reference_min_y", &y) != tinyxml2::XML_SUCCESS) { + y = 0.0f; + } + if(e->QueryFloatAttribute("reference_min_z", &z) != tinyxml2::XML_SUCCESS) { + z = 0.0f; + } + + m_reference.min = Vector3::Create(x,y,z); + + x=0.0f; y=0.0f; z=0.0f; + if(e->QueryFloatAttribute("reference_max_x", &x) != tinyxml2::XML_SUCCESS) { + x = 0.0f; + } + if(e->QueryFloatAttribute("reference_max_y", &y) != tinyxml2::XML_SUCCESS) { + y = 0.0f; + } + if(e->QueryFloatAttribute("reference_max_z", &z) != tinyxml2::XML_SUCCESS) { + z = 0.0f; + } + m_reference.max = Vector3::Create(x,y,z); + + m_use_world_units = true; + if(e->QueryBoolAttribute("use_world_units", &m_use_world_units) != tinyxml2::XML_SUCCESS) { + m_use_world_units = true; + } +} + + +const AABB &KRLODGroup::getReference() const +{ + return m_reference; +} + +void KRLODGroup::setReference(const AABB &reference) +{ + m_reference = reference; +} + +KRNode::LodVisibility KRLODGroup::calcLODVisibility(const KRViewport &viewport) +{ + if(m_min_distance == 0 && m_max_distance == 0) { + return LOD_VISIBILITY_VISIBLE; + } else { + float lod_bias = viewport.getLODBias(); + lod_bias = pow(2.0f, -lod_bias); + + // Compare using squared distances as sqrt is expensive + float sqr_distance; + float sqr_prestream_distance; + + Vector3 world_camera_position = viewport.getCameraPosition(); + Vector3 local_camera_position = worldToLocal(world_camera_position); + Vector3 local_reference_point = m_reference.nearestPoint(local_camera_position); + + if(m_use_world_units) { + Vector3 world_reference_point = localToWorld(local_reference_point); + sqr_distance = (world_camera_position - world_reference_point).sqrMagnitude() * (lod_bias * lod_bias); + sqr_prestream_distance = getContext().KRENGINE_PRESTREAM_DISTANCE * getContext().KRENGINE_PRESTREAM_DISTANCE; + } else { + sqr_distance = (local_camera_position - local_reference_point).sqrMagnitude() * (lod_bias * lod_bias); + + Vector3 world_reference_point = localToWorld(local_reference_point); + sqr_prestream_distance = worldToLocal(Vector3::Normalize(world_reference_point - world_camera_position) * getContext().KRENGINE_PRESTREAM_DISTANCE).sqrMagnitude(); // TODO, FINDME - Optimize with precalc? + + } + + float sqr_min_visible_distance = m_min_distance * m_min_distance; + float sqr_max_visible_distance = m_max_distance * m_max_distance; + if((sqr_distance >= sqr_min_visible_distance || m_min_distance == 0) && (sqr_distance < sqr_max_visible_distance || m_max_distance == 0)) { + return LOD_VISIBILITY_VISIBLE; + } else if((sqr_distance >= sqr_min_visible_distance - sqr_prestream_distance || m_min_distance == 0) && (sqr_distance < sqr_max_visible_distance + sqr_prestream_distance || m_max_distance == 0)) { + return LOD_VISIBILITY_PRESTREAM; + } else { + return LOD_VISIBILITY_HIDDEN; + } + } +} + +float KRLODGroup::getMinDistance() +{ + return m_min_distance; +} + +float KRLODGroup::getMaxDistance() +{ + return m_max_distance; +} + +void KRLODGroup::setMinDistance(float min_distance) +{ + m_min_distance = min_distance; +} + +void KRLODGroup::setMaxDistance(float max_distance) +{ + m_max_distance = max_distance; +} + +void KRLODGroup::setUseWorldUnits(bool use_world_units) +{ + m_use_world_units = use_world_units; +} + +bool KRLODGroup::getUseWorldUnits() const +{ + return m_use_world_units; +} diff --git a/kraken/KRLight.cpp b/kraken/KRLight.cpp index 9982695..528fb4d 100755 --- a/kraken/KRLight.cpp +++ b/kraken/KRLight.cpp @@ -1,485 +1,485 @@ -// -// KRLight.cpp -// KREngine -// -// Created by Kearwood Gilbert on 12-04-05. -// Copyright (c) 2012 Kearwood Software. All rights reserved. -// - - -#include "KREngine-common.h" -#include "KRLight.h" - -#include "KRNode.h" -#include "KRCamera.h" -#include "KRContext.h" - -#include "KRShaderManager.h" -#include "KRShader.h" -#include "KRStockGeometry.h" -#include "KRDirectionalLight.h" -#include "KRSpotLight.h" -#include "KRPointLight.h" - - -KRLight::KRLight(KRScene &scene, std::string name) : KRNode(scene, name) -{ - m_intensity = 1.0f; - m_dust_particle_intensity = 1.0f; - m_color = Vector3::One(); - m_flareTexture = ""; - m_pFlareTexture = NULL; - m_flareSize = 0.0; - m_flareOcclusionSize = 0.05; - m_casts_shadow = true; - m_light_shafts = true; - m_dust_particle_density = 0.1f; - m_dust_particle_size = 1.0f; - m_occlusionQuery = 0; - - // Initialize shadow buffers - m_cShadowBuffers = 0; - for(int iBuffer=0; iBuffer < KRENGINE_MAX_SHADOW_BUFFERS; iBuffer++) { - shadowFramebuffer[iBuffer] = 0; - shadowDepthTexture[iBuffer] = 0; - shadowValid[iBuffer] = false; - } -} - -KRLight::~KRLight() -{ - if(m_occlusionQuery) { - GLDEBUG(glDeleteQueriesEXT(1, &m_occlusionQuery)); - m_occlusionQuery = 0; - } - allocateShadowBuffers(0); -} - -tinyxml2::XMLElement *KRLight::saveXML( tinyxml2::XMLNode *parent) -{ - tinyxml2::XMLElement *e = KRNode::saveXML(parent); - e->SetAttribute("intensity", m_intensity); - e->SetAttribute("color_r", m_color.x); - e->SetAttribute("color_g", m_color.y); - e->SetAttribute("color_b", m_color.z); - e->SetAttribute("decay_start", m_decayStart); - e->SetAttribute("flare_size", m_flareSize); - e->SetAttribute("flare_occlusion_size", m_flareOcclusionSize); - e->SetAttribute("flare_texture", m_flareTexture.c_str()); - e->SetAttribute("casts_shadow", m_casts_shadow ? "true" : "false"); - e->SetAttribute("light_shafts", m_light_shafts ? "true" : "false"); - e->SetAttribute("dust_particle_density", m_dust_particle_density); - e->SetAttribute("dust_particle_size", m_dust_particle_size); - e->SetAttribute("dust_particle_intensity", m_dust_particle_intensity); - return e; -} - -void KRLight::loadXML(tinyxml2::XMLElement *e) { - KRNode::loadXML(e); - float x=1.0f,y=1.0f,z=1.0f; - if(e->QueryFloatAttribute("color_r", &x) != tinyxml2::XML_SUCCESS) { - x = 1.0; - } - if(e->QueryFloatAttribute("color_g", &y) != tinyxml2::XML_SUCCESS) { - y = 1.0; - } - if(e->QueryFloatAttribute("color_b", &z) != tinyxml2::XML_SUCCESS) { - z = 1.0; - } - m_color = Vector3(x,y,z); - - if(e->QueryFloatAttribute("intensity", &m_intensity) != tinyxml2::XML_SUCCESS) { - m_intensity = 100.0; - } - - if(e->QueryFloatAttribute("decay_start", &m_decayStart) != tinyxml2::XML_SUCCESS) { - m_decayStart = 0.0; - } - - if(e->QueryFloatAttribute("flare_size", &m_flareSize) != tinyxml2::XML_SUCCESS) { - m_flareSize = 0.0; - } - - if(e->QueryFloatAttribute("flare_occlusion_size", &m_flareOcclusionSize) != tinyxml2::XML_SUCCESS) { - m_flareOcclusionSize = 0.05; - } - - if(e->QueryBoolAttribute("casts_shadow", &m_casts_shadow) != tinyxml2::XML_SUCCESS) { - m_casts_shadow = true; - } - - if(e->QueryBoolAttribute("light_shafts", &m_light_shafts) != tinyxml2::XML_SUCCESS) { - m_light_shafts = true; - } - - m_dust_particle_density = 0.1f; - if(e->QueryFloatAttribute("dust_particle_density", &m_dust_particle_density) != tinyxml2::XML_SUCCESS) { - m_dust_particle_density = 0.1f; - } - - m_dust_particle_size = 1.0f; - if(e->QueryFloatAttribute("dust_particle_size", &m_dust_particle_size) != tinyxml2::XML_SUCCESS) { - m_dust_particle_size = 1.0f; - } - - m_dust_particle_intensity = 1.0f; - if(e->QueryFloatAttribute("dust_particle_intensity", &m_dust_particle_intensity) != tinyxml2::XML_SUCCESS) { - m_dust_particle_intensity = 1.0f; - } - - const char *szFlareTexture = e->Attribute("flare_texture"); - if(szFlareTexture) { - m_flareTexture = szFlareTexture; - } else { - m_flareTexture = ""; - } - m_pFlareTexture = NULL; -} - -void KRLight::setFlareTexture(std::string flare_texture) { - m_flareTexture = flare_texture; - m_pFlareTexture = NULL; -} - -void KRLight::setFlareSize(float flare_size) { - m_flareSize = flare_size; -} - -void KRLight::setFlareOcclusionSize(float occlusion_size) { - m_flareOcclusionSize = occlusion_size; -} - -void KRLight::setIntensity(float intensity) { - m_intensity = intensity; -} -float KRLight::getIntensity() { - return m_intensity; -} - -const Vector3 &KRLight::getColor() { - return m_color; -} - -void KRLight::setColor(const Vector3 &color) { - m_color = color; -} - -void KRLight::setDecayStart(float decayStart) { - m_decayStart = decayStart; -} - -float KRLight::getDecayStart() { - return m_decayStart; -} - -void KRLight::render(KRCamera *pCamera, std::vector &point_lights, std::vector &directional_lights, std::vector&spot_lights, const KRViewport &viewport, KRNode::RenderPass renderPass) { - - if(m_lod_visible <= LOD_VISIBILITY_PRESTREAM) return; - - KRNode::render(pCamera, point_lights, directional_lights, spot_lights, viewport, renderPass); - - if(renderPass == KRNode::RENDER_PASS_GENERATE_SHADOWMAPS && (pCamera->settings.volumetric_environment_enable || pCamera->settings.dust_particle_enable || (pCamera->settings.m_cShadowBuffers > 0 && m_casts_shadow))) { - allocateShadowBuffers(configureShadowBufferViewports(viewport)); - renderShadowBuffers(pCamera); - } - - if(renderPass == KRNode::RENDER_PASS_ADDITIVE_PARTICLES && pCamera->settings.dust_particle_enable) { - // Render brownian particles for dust floating in air - if(m_cShadowBuffers >= 1 && shadowValid[0] && m_dust_particle_density > 0.0f && m_dust_particle_size > 0.0f && m_dust_particle_intensity > 0.0f) { - - if(viewport.visible(getBounds()) || true) { // FINDME, HACK need to remove "|| true"? - - float particle_range = 600.0f; - - int particle_count = m_dust_particle_density * pow(particle_range, 3); - if(particle_count > KRMeshManager::KRENGINE_MAX_RANDOM_PARTICLES) particle_count = KRMeshManager::KRENGINE_MAX_RANDOM_PARTICLES; - - // Enable z-buffer test - GLDEBUG(glEnable(GL_DEPTH_TEST)); - GLDEBUG(glDepthRangef(0.0, 1.0)); - - Matrix4 particleModelMatrix; - particleModelMatrix.scale(particle_range); // Scale the box symetrically to ensure that we don't have an uneven distribution of particles for different angles of the view frustrum - particleModelMatrix.translate(viewport.getCameraPosition()); - - std::vector this_directional_light; - std::vector this_spot_light; - std::vector this_point_light; - KRDirectionalLight *directional_light = dynamic_cast(this); - KRSpotLight *spot_light = dynamic_cast(this); - KRPointLight *point_light = dynamic_cast(this); - if(directional_light) { - this_directional_light.push_back(directional_light); - } - if(spot_light) { - this_spot_light.push_back(spot_light); - } - if(point_light) { - this_point_light.push_back(point_light); - } - - KRShader *pParticleShader = m_pContext->getShaderManager()->getShader("dust_particle", pCamera, this_point_light, this_directional_light, this_spot_light, 0, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, renderPass); - - if(getContext().getShaderManager()->selectShader(*pCamera, pParticleShader, viewport, particleModelMatrix, this_point_light, this_directional_light, this_spot_light, 0, renderPass, Vector3::Zero(), 0.0f, Vector4::Zero())) { - - pParticleShader->setUniform(KRShader::KRENGINE_UNIFORM_LIGHT_COLOR, m_color * pCamera->settings.dust_particle_intensity * m_dust_particle_intensity * m_intensity); - pParticleShader->setUniform(KRShader::KRENGINE_UNIFORM_PARTICLE_ORIGIN, Matrix4::DotWDiv(Matrix4::Invert(particleModelMatrix), Vector3::Zero())); - pParticleShader->setUniform(KRShader::KRENGINE_UNIFORM_FLARE_SIZE, m_dust_particle_size); - - KRDataBlock particle_index_data; - m_pContext->getMeshManager()->bindVBO(m_pContext->getMeshManager()->getRandomParticles(), particle_index_data, (1 << KRMesh::KRENGINE_ATTRIB_VERTEX) | (1 << KRMesh::KRENGINE_ATTRIB_TEXUVA), true, 1.0f); - GLDEBUG(glDrawArrays(GL_TRIANGLES, 0, particle_count*3)); - } - } - } - } - - - if(renderPass == KRNode::RENDER_PASS_VOLUMETRIC_EFFECTS_ADDITIVE && pCamera->settings.volumetric_environment_enable && m_light_shafts) { - std::string shader_name = pCamera->settings.volumetric_environment_downsample != 0 ? "volumetric_fog_downsampled" : "volumetric_fog"; - - std::vector this_directional_light; - std::vector this_spot_light; - std::vector this_point_light; - KRDirectionalLight *directional_light = dynamic_cast(this); - KRSpotLight *spot_light = dynamic_cast(this); - KRPointLight *point_light = dynamic_cast(this); - if(directional_light) { - this_directional_light.push_back(directional_light); - } - if(spot_light) { - this_spot_light.push_back(spot_light); - } - if(point_light) { - this_point_light.push_back(point_light); - } - - KRShader *pFogShader = m_pContext->getShaderManager()->getShader(shader_name, pCamera, this_point_light, this_directional_light, this_spot_light, 0, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, KRNode::RENDER_PASS_ADDITIVE_PARTICLES); - - if(getContext().getShaderManager()->selectShader(*pCamera, pFogShader, viewport, Matrix4(), this_point_light, this_directional_light, this_spot_light, 0, KRNode::RENDER_PASS_VOLUMETRIC_EFFECTS_ADDITIVE, Vector3::Zero(), 0.0f, Vector4::Zero())) { - int slice_count = (int)(pCamera->settings.volumetric_environment_quality * 495.0) + 5; - - float slice_near = -pCamera->settings.getPerspectiveNearZ(); - float slice_far = -pCamera->settings.volumetric_environment_max_distance; - float slice_spacing = (slice_far - slice_near) / slice_count; - - pFogShader->setUniform(KRShader::KRENGINE_UNIFORM_SLICE_DEPTH_SCALE, Vector2(slice_near, slice_spacing)); - pFogShader->setUniform(KRShader::KRENGINE_UNIFORM_LIGHT_COLOR, (m_color * pCamera->settings.volumetric_environment_intensity * m_intensity * -slice_spacing / 1000.0f)); - - KRDataBlock index_data; - m_pContext->getMeshManager()->bindVBO(m_pContext->getMeshManager()->getVolumetricLightingVertexes(), index_data, (1 << KRMesh::KRENGINE_ATTRIB_VERTEX), true, 1.0f); - GLDEBUG(glDrawArrays(GL_TRIANGLES, 0, slice_count*6)); - } - - } - - if(renderPass == KRNode::RENDER_PASS_PARTICLE_OCCLUSION) { - if(m_flareTexture.size() && m_flareSize > 0.0f) { - - - Matrix4 occlusion_test_sphere_matrix = Matrix4(); - occlusion_test_sphere_matrix.scale(m_localScale * m_flareOcclusionSize); - occlusion_test_sphere_matrix.translate(m_localTranslation); - if(m_parentNode) { - occlusion_test_sphere_matrix *= m_parentNode->getModelMatrix(); - } - - if(getContext().getShaderManager()->selectShader("occlusion_test", *pCamera, point_lights, directional_lights, spot_lights, 0, viewport, occlusion_test_sphere_matrix, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, renderPass, Vector3::Zero(), 0.0f, Vector4::Zero())) { - - GLDEBUG(glGenQueriesEXT(1, &m_occlusionQuery)); -#if TARGET_OS_IPHONE - GLDEBUG(glBeginQueryEXT(GL_ANY_SAMPLES_PASSED_EXT, m_occlusionQuery)); -#else - GLDEBUG(glBeginQuery(GL_SAMPLES_PASSED, m_occlusionQuery)); -#endif - - std::vector sphereModels = getContext().getMeshManager()->getModel("__sphere"); - if(sphereModels.size()) { - for(int i=0; i < sphereModels[0]->getSubmeshCount(); i++) { - sphereModels[0]->renderSubmesh(i, renderPass, getName(), "occlusion_test", 1.0f); - } - } - -#if TARGET_OS_IPHONE - GLDEBUG(glEndQueryEXT(GL_ANY_SAMPLES_PASSED_EXT)); -#else - GLDEBUG(glEndQuery(GL_SAMPLES_PASSED)); -#endif - - } - } - } - - if(renderPass == KRNode::RENDER_PASS_ADDITIVE_PARTICLES) { - if(m_flareTexture.size() && m_flareSize > 0.0f) { - - if(m_occlusionQuery) { - GLuint params = 0; - GLDEBUG(glGetQueryObjectuivEXT(m_occlusionQuery, GL_QUERY_RESULT_EXT, ¶ms)); - GLDEBUG(glDeleteQueriesEXT(1, &m_occlusionQuery)); - - if(params) { - - if(!m_pFlareTexture && m_flareTexture.size()) { - m_pFlareTexture = getContext().getTextureManager()->getTexture(m_flareTexture); - } - - if(m_pFlareTexture) { - // Disable z-buffer test - GLDEBUG(glDisable(GL_DEPTH_TEST)); - GLDEBUG(glDepthRangef(0.0, 1.0)); - - // Render light flare on transparency pass - KRShader *pShader = getContext().getShaderManager()->getShader("flare", pCamera, point_lights, directional_lights, spot_lights, 0, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, renderPass); - - if(getContext().getShaderManager()->selectShader(*pCamera, pShader, viewport, getModelMatrix(), point_lights, directional_lights, spot_lights, 0, renderPass, Vector3::Zero(), 0.0f, Vector4::Zero())) { - pShader->setUniform(KRShader::KRENGINE_UNIFORM_MATERIAL_ALPHA, 1.0f); - pShader->setUniform(KRShader::KRENGINE_UNIFORM_FLARE_SIZE, m_flareSize); - m_pContext->getTextureManager()->selectTexture(0, m_pFlareTexture, 0.0f, KRTexture::TEXTURE_USAGE_LIGHT_FLARE); - m_pContext->getMeshManager()->bindVBO(&getContext().getMeshManager()->KRENGINE_VBO_DATA_2D_SQUARE_VERTICES, 1.0f); - GLDEBUG(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)); - } - } - } - } - } - - } -} - -void KRLight::allocateShadowBuffers(int cBuffers) { - // First deallocate buffers no longer needed - for(int iShadow = cBuffers; iShadow < KRENGINE_MAX_SHADOW_BUFFERS; iShadow++) { - if (shadowDepthTexture[iShadow]) { - GLDEBUG(glDeleteTextures(1, shadowDepthTexture + iShadow)); - shadowDepthTexture[iShadow] = 0; - } - - if (shadowFramebuffer[iShadow]) { - GLDEBUG(glDeleteFramebuffers(1, shadowFramebuffer + iShadow)); - shadowFramebuffer[iShadow] = 0; - } - } - - // Allocate newly required buffers - for(int iShadow = 0; iShadow < cBuffers; iShadow++) { - Vector2 viewportSize = m_shadowViewports[iShadow].getSize(); - - if(!shadowDepthTexture[iShadow]) { - shadowValid[iShadow] = false; - - GLDEBUG(glGenFramebuffers(1, shadowFramebuffer + iShadow)); - GLDEBUG(glGenTextures(1, shadowDepthTexture + iShadow)); - // ===== Create offscreen shadow framebuffer object ===== - - GLDEBUG(glBindFramebuffer(GL_FRAMEBUFFER, shadowFramebuffer[iShadow])); - - // ----- Create Depth Texture for shadowFramebuffer ----- - GLDEBUG( glBindTexture(GL_TEXTURE_2D, shadowDepthTexture[iShadow])); - GLDEBUG(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)); - GLDEBUG(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)); - m_pContext->getTextureManager()->_setWrapModeS(shadowDepthTexture[iShadow], GL_CLAMP_TO_EDGE); - m_pContext->getTextureManager()->_setWrapModeT(shadowDepthTexture[iShadow], GL_CLAMP_TO_EDGE); -#if GL_EXT_shadow_samplers - GLDEBUG(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_EXT, GL_COMPARE_REF_TO_TEXTURE_EXT)); // TODO - Detect GL_EXT_shadow_samplers and only activate if available - GLDEBUG(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC_EXT, GL_LEQUAL)); // TODO - Detect GL_EXT_shadow_samplers and only activate if available -#endif - GLDEBUG(glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, viewportSize.x, viewportSize.y, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL)); - - GLDEBUG(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, shadowDepthTexture[iShadow], 0)); - } - } - - m_cShadowBuffers = cBuffers; -} - - -void KRLight::deleteBuffers() -{ - // Called when this light wasn't used in the last frame, so we can free the resources for use by other lights - allocateShadowBuffers(0); -} - -void KRLight::invalidateShadowBuffers() -{ - for(int iShadow=0; iShadow < m_cShadowBuffers; iShadow++) { - shadowValid[iShadow] = false; - } -} - -int KRLight::configureShadowBufferViewports(const KRViewport &viewport) -{ - return 0; -} - -void KRLight::renderShadowBuffers(KRCamera *pCamera) -{ - for(int iShadow=0; iShadow < m_cShadowBuffers; iShadow++) { - if(!shadowValid[iShadow]) { - shadowValid[iShadow] = true; - - GLDEBUG(glBindFramebuffer(GL_FRAMEBUFFER, shadowFramebuffer[iShadow])); - GLDEBUG(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, shadowDepthTexture[iShadow], 0)); - - GLDEBUG(glViewport(0, 0, m_shadowViewports[iShadow].getSize().x, m_shadowViewports[iShadow].getSize().y)); - - GLDEBUG(glClearDepthf(0.0f)); - GLDEBUG(glClear(GL_DEPTH_BUFFER_BIT)); - - GLDEBUG(glViewport(1, 1, m_shadowViewports[iShadow].getSize().x - 2, m_shadowViewports[iShadow].getSize().y - 2)); - - GLDEBUG(glClearDepthf(1.0f)); - - GLDEBUG(glClear(GL_DEPTH_BUFFER_BIT)); - - GLDEBUG(glDisable(GL_DITHER)); - - //GLDEBUG(glCullFace(GL_BACK)); // Enable frontface culling, which eliminates some self-cast shadow artifacts - //GLDEBUG(glEnable(GL_CULL_FACE)); - GLDEBUG(glDisable(GL_CULL_FACE)); - - // Enable z-buffer test - GLDEBUG(glEnable(GL_DEPTH_TEST)); - GLDEBUG(glDepthFunc(GL_LESS)); - GLDEBUG(glDepthRangef(0.0, 1.0)); - - // Disable alpha blending as we are using alpha channel for packed depth info - GLDEBUG(glDisable(GL_BLEND)); - - // Use shader program - KRShader *shadowShader = m_pContext->getShaderManager()->getShader("ShadowShader", pCamera, std::vector(), std::vector(), std::vector(), 0, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, KRNode::RENDER_PASS_FORWARD_TRANSPARENT); - - getContext().getShaderManager()->selectShader(*pCamera, shadowShader, m_shadowViewports[iShadow], Matrix4(), std::vector(), std::vector(), std::vector(), 0, KRNode::RENDER_PASS_SHADOWMAP, Vector3::Zero(), 0.0f, Vector4::Zero()); - - - getScene().render(pCamera, m_shadowViewports[iShadow].getVisibleBounds(), m_shadowViewports[iShadow], KRNode::RENDER_PASS_SHADOWMAP, true); - - GLDEBUG(glEnable(GL_CULL_FACE)); - } - } -} - - - -int KRLight::getShadowBufferCount() -{ - int cBuffers=0; - for(int iBuffer=0; iBuffer < m_cShadowBuffers; iBuffer++) { - if(shadowValid[iBuffer]) { - cBuffers++; - } else { - break; - } - } - return cBuffers; -} - -GLuint *KRLight::getShadowTextures() -{ - return shadowDepthTexture; -} - -KRViewport *KRLight::getShadowViewports() -{ - return m_shadowViewports; -} +// +// KRLight.cpp +// KREngine +// +// Created by Kearwood Gilbert on 12-04-05. +// Copyright (c) 2012 Kearwood Software. All rights reserved. +// + + +#include "KREngine-common.h" +#include "KRLight.h" + +#include "KRNode.h" +#include "KRCamera.h" +#include "KRContext.h" + +#include "KRShaderManager.h" +#include "KRShader.h" +#include "KRStockGeometry.h" +#include "KRDirectionalLight.h" +#include "KRSpotLight.h" +#include "KRPointLight.h" + + +KRLight::KRLight(KRScene &scene, std::string name) : KRNode(scene, name) +{ + m_intensity = 1.0f; + m_dust_particle_intensity = 1.0f; + m_color = Vector3::One(); + m_flareTexture = ""; + m_pFlareTexture = NULL; + m_flareSize = 0.0f; + m_flareOcclusionSize = 0.05f; + m_casts_shadow = true; + m_light_shafts = true; + m_dust_particle_density = 0.1f; + m_dust_particle_size = 1.0f; + m_occlusionQuery = 0; + + // Initialize shadow buffers + m_cShadowBuffers = 0; + for(int iBuffer=0; iBuffer < KRENGINE_MAX_SHADOW_BUFFERS; iBuffer++) { + shadowFramebuffer[iBuffer] = 0; + shadowDepthTexture[iBuffer] = 0; + shadowValid[iBuffer] = false; + } +} + +KRLight::~KRLight() +{ + if(m_occlusionQuery) { + GLDEBUG(glDeleteQueriesEXT(1, &m_occlusionQuery)); + m_occlusionQuery = 0; + } + allocateShadowBuffers(0); +} + +tinyxml2::XMLElement *KRLight::saveXML( tinyxml2::XMLNode *parent) +{ + tinyxml2::XMLElement *e = KRNode::saveXML(parent); + e->SetAttribute("intensity", m_intensity); + e->SetAttribute("color_r", m_color.x); + e->SetAttribute("color_g", m_color.y); + e->SetAttribute("color_b", m_color.z); + e->SetAttribute("decay_start", m_decayStart); + e->SetAttribute("flare_size", m_flareSize); + e->SetAttribute("flare_occlusion_size", m_flareOcclusionSize); + e->SetAttribute("flare_texture", m_flareTexture.c_str()); + e->SetAttribute("casts_shadow", m_casts_shadow ? "true" : "false"); + e->SetAttribute("light_shafts", m_light_shafts ? "true" : "false"); + e->SetAttribute("dust_particle_density", m_dust_particle_density); + e->SetAttribute("dust_particle_size", m_dust_particle_size); + e->SetAttribute("dust_particle_intensity", m_dust_particle_intensity); + return e; +} + +void KRLight::loadXML(tinyxml2::XMLElement *e) { + KRNode::loadXML(e); + float x=1.0f,y=1.0f,z=1.0f; + if(e->QueryFloatAttribute("color_r", &x) != tinyxml2::XML_SUCCESS) { + x = 1.0; + } + if(e->QueryFloatAttribute("color_g", &y) != tinyxml2::XML_SUCCESS) { + y = 1.0; + } + if(e->QueryFloatAttribute("color_b", &z) != tinyxml2::XML_SUCCESS) { + z = 1.0; + } + m_color = Vector3::Create(x,y,z); + + if(e->QueryFloatAttribute("intensity", &m_intensity) != tinyxml2::XML_SUCCESS) { + m_intensity = 100.0; + } + + if(e->QueryFloatAttribute("decay_start", &m_decayStart) != tinyxml2::XML_SUCCESS) { + m_decayStart = 0.0; + } + + if(e->QueryFloatAttribute("flare_size", &m_flareSize) != tinyxml2::XML_SUCCESS) { + m_flareSize = 0.0; + } + + if(e->QueryFloatAttribute("flare_occlusion_size", &m_flareOcclusionSize) != tinyxml2::XML_SUCCESS) { + m_flareOcclusionSize = 0.05f; + } + + if(e->QueryBoolAttribute("casts_shadow", &m_casts_shadow) != tinyxml2::XML_SUCCESS) { + m_casts_shadow = true; + } + + if(e->QueryBoolAttribute("light_shafts", &m_light_shafts) != tinyxml2::XML_SUCCESS) { + m_light_shafts = true; + } + + m_dust_particle_density = 0.1f; + if(e->QueryFloatAttribute("dust_particle_density", &m_dust_particle_density) != tinyxml2::XML_SUCCESS) { + m_dust_particle_density = 0.1f; + } + + m_dust_particle_size = 1.0f; + if(e->QueryFloatAttribute("dust_particle_size", &m_dust_particle_size) != tinyxml2::XML_SUCCESS) { + m_dust_particle_size = 1.0f; + } + + m_dust_particle_intensity = 1.0f; + if(e->QueryFloatAttribute("dust_particle_intensity", &m_dust_particle_intensity) != tinyxml2::XML_SUCCESS) { + m_dust_particle_intensity = 1.0f; + } + + const char *szFlareTexture = e->Attribute("flare_texture"); + if(szFlareTexture) { + m_flareTexture = szFlareTexture; + } else { + m_flareTexture = ""; + } + m_pFlareTexture = NULL; +} + +void KRLight::setFlareTexture(std::string flare_texture) { + m_flareTexture = flare_texture; + m_pFlareTexture = NULL; +} + +void KRLight::setFlareSize(float flare_size) { + m_flareSize = flare_size; +} + +void KRLight::setFlareOcclusionSize(float occlusion_size) { + m_flareOcclusionSize = occlusion_size; +} + +void KRLight::setIntensity(float intensity) { + m_intensity = intensity; +} +float KRLight::getIntensity() { + return m_intensity; +} + +const Vector3 &KRLight::getColor() { + return m_color; +} + +void KRLight::setColor(const Vector3 &color) { + m_color = color; +} + +void KRLight::setDecayStart(float decayStart) { + m_decayStart = decayStart; +} + +float KRLight::getDecayStart() { + return m_decayStart; +} + +void KRLight::render(KRCamera *pCamera, std::vector &point_lights, std::vector &directional_lights, std::vector&spot_lights, const KRViewport &viewport, KRNode::RenderPass renderPass) { + + if(m_lod_visible <= LOD_VISIBILITY_PRESTREAM) return; + + KRNode::render(pCamera, point_lights, directional_lights, spot_lights, viewport, renderPass); + + if(renderPass == KRNode::RENDER_PASS_GENERATE_SHADOWMAPS && (pCamera->settings.volumetric_environment_enable || pCamera->settings.dust_particle_enable || (pCamera->settings.m_cShadowBuffers > 0 && m_casts_shadow))) { + allocateShadowBuffers(configureShadowBufferViewports(viewport)); + renderShadowBuffers(pCamera); + } + + if(renderPass == KRNode::RENDER_PASS_ADDITIVE_PARTICLES && pCamera->settings.dust_particle_enable) { + // Render brownian particles for dust floating in air + if(m_cShadowBuffers >= 1 && shadowValid[0] && m_dust_particle_density > 0.0f && m_dust_particle_size > 0.0f && m_dust_particle_intensity > 0.0f) { + + if(viewport.visible(getBounds()) || true) { // FINDME, HACK need to remove "|| true"? + + float particle_range = 600.0f; + + int particle_count = m_dust_particle_density * pow(particle_range, 3); + if(particle_count > KRMeshManager::KRENGINE_MAX_RANDOM_PARTICLES) particle_count = KRMeshManager::KRENGINE_MAX_RANDOM_PARTICLES; + + // Enable z-buffer test + GLDEBUG(glEnable(GL_DEPTH_TEST)); + GLDEBUG(glDepthRangef(0.0, 1.0)); + + Matrix4 particleModelMatrix; + particleModelMatrix.scale(particle_range); // Scale the box symetrically to ensure that we don't have an uneven distribution of particles for different angles of the view frustrum + particleModelMatrix.translate(viewport.getCameraPosition()); + + std::vector this_directional_light; + std::vector this_spot_light; + std::vector this_point_light; + KRDirectionalLight *directional_light = dynamic_cast(this); + KRSpotLight *spot_light = dynamic_cast(this); + KRPointLight *point_light = dynamic_cast(this); + if(directional_light) { + this_directional_light.push_back(directional_light); + } + if(spot_light) { + this_spot_light.push_back(spot_light); + } + if(point_light) { + this_point_light.push_back(point_light); + } + + KRShader *pParticleShader = m_pContext->getShaderManager()->getShader("dust_particle", pCamera, this_point_light, this_directional_light, this_spot_light, 0, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, renderPass); + + if(getContext().getShaderManager()->selectShader(*pCamera, pParticleShader, viewport, particleModelMatrix, this_point_light, this_directional_light, this_spot_light, 0, renderPass, Vector3::Zero(), 0.0f, Vector4::Zero())) { + + pParticleShader->setUniform(KRShader::KRENGINE_UNIFORM_LIGHT_COLOR, m_color * pCamera->settings.dust_particle_intensity * m_dust_particle_intensity * m_intensity); + pParticleShader->setUniform(KRShader::KRENGINE_UNIFORM_PARTICLE_ORIGIN, Matrix4::DotWDiv(Matrix4::Invert(particleModelMatrix), Vector3::Zero())); + pParticleShader->setUniform(KRShader::KRENGINE_UNIFORM_FLARE_SIZE, m_dust_particle_size); + + KRDataBlock particle_index_data; + m_pContext->getMeshManager()->bindVBO(m_pContext->getMeshManager()->getRandomParticles(), particle_index_data, (1 << KRMesh::KRENGINE_ATTRIB_VERTEX) | (1 << KRMesh::KRENGINE_ATTRIB_TEXUVA), true, 1.0f); + GLDEBUG(glDrawArrays(GL_TRIANGLES, 0, particle_count*3)); + } + } + } + } + + + if(renderPass == KRNode::RENDER_PASS_VOLUMETRIC_EFFECTS_ADDITIVE && pCamera->settings.volumetric_environment_enable && m_light_shafts) { + std::string shader_name = pCamera->settings.volumetric_environment_downsample != 0 ? "volumetric_fog_downsampled" : "volumetric_fog"; + + std::vector this_directional_light; + std::vector this_spot_light; + std::vector this_point_light; + KRDirectionalLight *directional_light = dynamic_cast(this); + KRSpotLight *spot_light = dynamic_cast(this); + KRPointLight *point_light = dynamic_cast(this); + if(directional_light) { + this_directional_light.push_back(directional_light); + } + if(spot_light) { + this_spot_light.push_back(spot_light); + } + if(point_light) { + this_point_light.push_back(point_light); + } + + KRShader *pFogShader = m_pContext->getShaderManager()->getShader(shader_name, pCamera, this_point_light, this_directional_light, this_spot_light, 0, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, KRNode::RENDER_PASS_ADDITIVE_PARTICLES); + + if(getContext().getShaderManager()->selectShader(*pCamera, pFogShader, viewport, Matrix4(), this_point_light, this_directional_light, this_spot_light, 0, KRNode::RENDER_PASS_VOLUMETRIC_EFFECTS_ADDITIVE, Vector3::Zero(), 0.0f, Vector4::Zero())) { + int slice_count = (int)(pCamera->settings.volumetric_environment_quality * 495.0) + 5; + + float slice_near = -pCamera->settings.getPerspectiveNearZ(); + float slice_far = -pCamera->settings.volumetric_environment_max_distance; + float slice_spacing = (slice_far - slice_near) / slice_count; + + pFogShader->setUniform(KRShader::KRENGINE_UNIFORM_SLICE_DEPTH_SCALE, Vector2::Create(slice_near, slice_spacing)); + pFogShader->setUniform(KRShader::KRENGINE_UNIFORM_LIGHT_COLOR, (m_color * pCamera->settings.volumetric_environment_intensity * m_intensity * -slice_spacing / 1000.0f)); + + KRDataBlock index_data; + m_pContext->getMeshManager()->bindVBO(m_pContext->getMeshManager()->getVolumetricLightingVertexes(), index_data, (1 << KRMesh::KRENGINE_ATTRIB_VERTEX), true, 1.0f); + GLDEBUG(glDrawArrays(GL_TRIANGLES, 0, slice_count*6)); + } + + } + + if(renderPass == KRNode::RENDER_PASS_PARTICLE_OCCLUSION) { + if(m_flareTexture.size() && m_flareSize > 0.0f) { + + + Matrix4 occlusion_test_sphere_matrix = Matrix4(); + occlusion_test_sphere_matrix.scale(m_localScale * m_flareOcclusionSize); + occlusion_test_sphere_matrix.translate(m_localTranslation); + if(m_parentNode) { + occlusion_test_sphere_matrix *= m_parentNode->getModelMatrix(); + } + + if(getContext().getShaderManager()->selectShader("occlusion_test", *pCamera, point_lights, directional_lights, spot_lights, 0, viewport, occlusion_test_sphere_matrix, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, renderPass, Vector3::Zero(), 0.0f, Vector4::Zero())) { + + GLDEBUG(glGenQueriesEXT(1, &m_occlusionQuery)); +#if TARGET_OS_IPHONE + GLDEBUG(glBeginQueryEXT(GL_ANY_SAMPLES_PASSED_EXT, m_occlusionQuery)); +#else + GLDEBUG(glBeginQuery(GL_SAMPLES_PASSED, m_occlusionQuery)); +#endif + + std::vector sphereModels = getContext().getMeshManager()->getModel("__sphere"); + if(sphereModels.size()) { + for(int i=0; i < sphereModels[0]->getSubmeshCount(); i++) { + sphereModels[0]->renderSubmesh(i, renderPass, getName(), "occlusion_test", 1.0f); + } + } + +#if TARGET_OS_IPHONE + GLDEBUG(glEndQueryEXT(GL_ANY_SAMPLES_PASSED_EXT)); +#else + GLDEBUG(glEndQuery(GL_SAMPLES_PASSED)); +#endif + + } + } + } + + if(renderPass == KRNode::RENDER_PASS_ADDITIVE_PARTICLES) { + if(m_flareTexture.size() && m_flareSize > 0.0f) { + + if(m_occlusionQuery) { + GLuint params = 0; + GLDEBUG(glGetQueryObjectuivEXT(m_occlusionQuery, GL_QUERY_RESULT_EXT, ¶ms)); + GLDEBUG(glDeleteQueriesEXT(1, &m_occlusionQuery)); + + if(params) { + + if(!m_pFlareTexture && m_flareTexture.size()) { + m_pFlareTexture = getContext().getTextureManager()->getTexture(m_flareTexture); + } + + if(m_pFlareTexture) { + // Disable z-buffer test + GLDEBUG(glDisable(GL_DEPTH_TEST)); + GLDEBUG(glDepthRangef(0.0, 1.0)); + + // Render light flare on transparency pass + KRShader *pShader = getContext().getShaderManager()->getShader("flare", pCamera, point_lights, directional_lights, spot_lights, 0, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, renderPass); + + if(getContext().getShaderManager()->selectShader(*pCamera, pShader, viewport, getModelMatrix(), point_lights, directional_lights, spot_lights, 0, renderPass, Vector3::Zero(), 0.0f, Vector4::Zero())) { + pShader->setUniform(KRShader::KRENGINE_UNIFORM_MATERIAL_ALPHA, 1.0f); + pShader->setUniform(KRShader::KRENGINE_UNIFORM_FLARE_SIZE, m_flareSize); + m_pContext->getTextureManager()->selectTexture(0, m_pFlareTexture, 0.0f, KRTexture::TEXTURE_USAGE_LIGHT_FLARE); + m_pContext->getMeshManager()->bindVBO(&getContext().getMeshManager()->KRENGINE_VBO_DATA_2D_SQUARE_VERTICES, 1.0f); + GLDEBUG(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)); + } + } + } + } + } + + } +} + +void KRLight::allocateShadowBuffers(int cBuffers) { + // First deallocate buffers no longer needed + for(int iShadow = cBuffers; iShadow < KRENGINE_MAX_SHADOW_BUFFERS; iShadow++) { + if (shadowDepthTexture[iShadow]) { + GLDEBUG(glDeleteTextures(1, shadowDepthTexture + iShadow)); + shadowDepthTexture[iShadow] = 0; + } + + if (shadowFramebuffer[iShadow]) { + GLDEBUG(glDeleteFramebuffers(1, shadowFramebuffer + iShadow)); + shadowFramebuffer[iShadow] = 0; + } + } + + // Allocate newly required buffers + for(int iShadow = 0; iShadow < cBuffers; iShadow++) { + Vector2 viewportSize = m_shadowViewports[iShadow].getSize(); + + if(!shadowDepthTexture[iShadow]) { + shadowValid[iShadow] = false; + + GLDEBUG(glGenFramebuffers(1, shadowFramebuffer + iShadow)); + GLDEBUG(glGenTextures(1, shadowDepthTexture + iShadow)); + // ===== Create offscreen shadow framebuffer object ===== + + GLDEBUG(glBindFramebuffer(GL_FRAMEBUFFER, shadowFramebuffer[iShadow])); + + // ----- Create Depth Texture for shadowFramebuffer ----- + GLDEBUG( glBindTexture(GL_TEXTURE_2D, shadowDepthTexture[iShadow])); + GLDEBUG(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)); + GLDEBUG(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)); + m_pContext->getTextureManager()->_setWrapModeS(shadowDepthTexture[iShadow], GL_CLAMP_TO_EDGE); + m_pContext->getTextureManager()->_setWrapModeT(shadowDepthTexture[iShadow], GL_CLAMP_TO_EDGE); +#if GL_EXT_shadow_samplers + GLDEBUG(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_EXT, GL_COMPARE_REF_TO_TEXTURE_EXT)); // TODO - Detect GL_EXT_shadow_samplers and only activate if available + GLDEBUG(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC_EXT, GL_LEQUAL)); // TODO - Detect GL_EXT_shadow_samplers and only activate if available +#endif + GLDEBUG(glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, viewportSize.x, viewportSize.y, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL)); + + GLDEBUG(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, shadowDepthTexture[iShadow], 0)); + } + } + + m_cShadowBuffers = cBuffers; +} + + +void KRLight::deleteBuffers() +{ + // Called when this light wasn't used in the last frame, so we can free the resources for use by other lights + allocateShadowBuffers(0); +} + +void KRLight::invalidateShadowBuffers() +{ + for(int iShadow=0; iShadow < m_cShadowBuffers; iShadow++) { + shadowValid[iShadow] = false; + } +} + +int KRLight::configureShadowBufferViewports(const KRViewport &viewport) +{ + return 0; +} + +void KRLight::renderShadowBuffers(KRCamera *pCamera) +{ + for(int iShadow=0; iShadow < m_cShadowBuffers; iShadow++) { + if(!shadowValid[iShadow]) { + shadowValid[iShadow] = true; + + GLDEBUG(glBindFramebuffer(GL_FRAMEBUFFER, shadowFramebuffer[iShadow])); + GLDEBUG(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, shadowDepthTexture[iShadow], 0)); + + GLDEBUG(glViewport(0, 0, m_shadowViewports[iShadow].getSize().x, m_shadowViewports[iShadow].getSize().y)); + + GLDEBUG(glClearDepthf(0.0f)); + GLDEBUG(glClear(GL_DEPTH_BUFFER_BIT)); + + GLDEBUG(glViewport(1, 1, m_shadowViewports[iShadow].getSize().x - 2, m_shadowViewports[iShadow].getSize().y - 2)); + + GLDEBUG(glClearDepthf(1.0f)); + + GLDEBUG(glClear(GL_DEPTH_BUFFER_BIT)); + + GLDEBUG(glDisable(GL_DITHER)); + + //GLDEBUG(glCullFace(GL_BACK)); // Enable frontface culling, which eliminates some self-cast shadow artifacts + //GLDEBUG(glEnable(GL_CULL_FACE)); + GLDEBUG(glDisable(GL_CULL_FACE)); + + // Enable z-buffer test + GLDEBUG(glEnable(GL_DEPTH_TEST)); + GLDEBUG(glDepthFunc(GL_LESS)); + GLDEBUG(glDepthRangef(0.0, 1.0)); + + // Disable alpha blending as we are using alpha channel for packed depth info + GLDEBUG(glDisable(GL_BLEND)); + + // Use shader program + KRShader *shadowShader = m_pContext->getShaderManager()->getShader("ShadowShader", pCamera, std::vector(), std::vector(), std::vector(), 0, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, KRNode::RENDER_PASS_FORWARD_TRANSPARENT); + + getContext().getShaderManager()->selectShader(*pCamera, shadowShader, m_shadowViewports[iShadow], Matrix4(), std::vector(), std::vector(), std::vector(), 0, KRNode::RENDER_PASS_SHADOWMAP, Vector3::Zero(), 0.0f, Vector4::Zero()); + + + getScene().render(pCamera, m_shadowViewports[iShadow].getVisibleBounds(), m_shadowViewports[iShadow], KRNode::RENDER_PASS_SHADOWMAP, true); + + GLDEBUG(glEnable(GL_CULL_FACE)); + } + } +} + + + +int KRLight::getShadowBufferCount() +{ + int cBuffers=0; + for(int iBuffer=0; iBuffer < m_cShadowBuffers; iBuffer++) { + if(shadowValid[iBuffer]) { + cBuffers++; + } else { + break; + } + } + return cBuffers; +} + +GLuint *KRLight::getShadowTextures() +{ + return shadowDepthTexture; +} + +KRViewport *KRLight::getShadowViewports() +{ + return m_shadowViewports; +} diff --git a/kraken/KRMaterial.cpp b/kraken/KRMaterial.cpp index dfb9ade..4b518d1 100755 --- a/kraken/KRMaterial.cpp +++ b/kraken/KRMaterial.cpp @@ -1,422 +1,422 @@ -// -// KRMaterial.cpp -// KREngine -// -// Copyright 2012 Kearwood Gilbert. All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, are -// permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of -// conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list -// of conditions and the following disclaimer in the documentation and/or other materials -// provided with the distribution. -// -// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED -// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR -// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// The views and conclusions contained in the software and documentation are those of the -// authors and should not be interpreted as representing official policies, either expressed -// or implied, of Kearwood Gilbert. -// - -#include "KREngine-common.h" - -#include "KRMaterial.h" -#include "KRTextureManager.h" - -#include "KRContext.h" - -KRMaterial::KRMaterial(KRContext &context, const char *szName) : KRResource(context, szName) { - m_name = szName; - m_pAmbientMap = NULL; - m_pDiffuseMap = NULL; - m_pSpecularMap = NULL; - m_pNormalMap = NULL; - m_pReflectionMap = NULL; - m_pReflectionCube = NULL; - m_ambientColor = Vector3::Zero(); - m_diffuseColor = Vector3::One(); - m_specularColor = Vector3::One(); - m_reflectionColor = Vector3::Zero(); - m_tr = (GLfloat)1.0f; - m_ns = (GLfloat)0.0f; - m_ambientMap = ""; - m_diffuseMap = ""; - m_specularMap = ""; - m_normalMap = ""; - m_reflectionMap = ""; - m_reflectionCube = ""; - m_ambientMapOffset = Vector2(0.0f, 0.0f); - m_specularMapOffset = Vector2(0.0f, 0.0f); - m_diffuseMapOffset = Vector2(0.0f, 0.0f); - m_ambientMapScale = Vector2(1.0f, 1.0f); - m_specularMapScale = Vector2(1.0f, 1.0f); - m_diffuseMapScale = Vector2(1.0f, 1.0f); - m_reflectionMapOffset = Vector2(0.0f, 0.0f); - m_reflectionMapScale = Vector2(1.0f, 1.0f); - m_alpha_mode = KRMATERIAL_ALPHA_MODE_OPAQUE; -} - -KRMaterial::~KRMaterial() { - -} - -std::string KRMaterial::getExtension() { - return "mtl"; -} - -bool KRMaterial::needsVertexTangents() -{ - return m_normalMap.size() > 0; -} - -bool KRMaterial::save(KRDataBlock &data) { - std::stringstream stream; - stream.precision(std::numeric_limits::digits10); - stream.setf(std::ios::fixed,std::ios::floatfield); - - stream << "newmtl " << m_name; - stream << "\nka " << m_ambientColor.x << " " << m_ambientColor.y << " " << m_ambientColor.z; - stream << "\nkd " << m_diffuseColor.x << " " << m_diffuseColor.y << " " << m_diffuseColor.z; - stream << "\nks " << m_specularColor.x << " " << m_specularColor.y << " " << m_specularColor.z; - stream << "\nkr " << m_reflectionColor.x << " " << m_reflectionColor.y << " " << m_reflectionColor.z; - stream << "\nTr " << m_tr; - stream << "\nNs " << m_ns; - if(m_ambientMap.size()) { - stream << "\nmap_Ka " << m_ambientMap << ".pvr -s " << m_ambientMapScale.x << " " << m_ambientMapScale.y << " -o " << m_ambientMapOffset.x << " " << m_ambientMapOffset.y; - } else { - stream << "\n# map_Ka filename.pvr -s 1.0 1.0 -o 0.0 0.0"; - } - if(m_diffuseMap.size()) { - stream << "\nmap_Kd " << m_diffuseMap << ".pvr -s " << m_diffuseMapScale.x << " " << m_diffuseMapScale.y << " -o " << m_diffuseMapOffset.x << " " << m_diffuseMapOffset.y; - } else { - stream << "\n# map_Kd filename.pvr -s 1.0 1.0 -o 0.0 0.0"; - } - if(m_specularMap.size()) { - stream << "\nmap_Ks " << m_specularMap << ".pvr -s " << m_specularMapScale.x << " " << m_specularMapScale.y << " -o " << m_specularMapOffset.x << " " << m_specularMapOffset.y << "\n"; - } else { - stream << "\n# map_Ks filename.pvr -s 1.0 1.0 -o 0.0 0.0"; - } - if(m_normalMap.size()) { - stream << "\nmap_Normal " << m_normalMap << ".pvr -s " << m_normalMapScale.x << " " << m_normalMapScale.y << " -o " << m_normalMapOffset.x << " " << m_normalMapOffset.y; - } else { - stream << "\n# map_Normal filename.pvr -s 1.0 1.0 -o 0.0 0.0"; - } - if(m_reflectionMap.size()) { - stream << "\nmap_Reflection " << m_reflectionMap << ".pvr -s " << m_reflectionMapScale.x << " " << m_reflectionMapScale.y << " -o " << m_reflectionMapOffset.x << " " << m_reflectionMapOffset.y; - } else { - stream << "\n# map_Reflection filename.pvr -s 1.0 1.0 -o 0.0 0.0"; - } - if(m_reflectionCube.size()) { - stream << "\nmap_ReflectionCube " << m_reflectionCube << ".pvr"; - } else { - stream << "\n# map_ReflectionCube cubemapname"; - } - switch(m_alpha_mode) { - case KRMATERIAL_ALPHA_MODE_OPAQUE: - stream << "\nalpha_mode opaque"; - break; - case KRMATERIAL_ALPHA_MODE_TEST: - stream << "\nalpha_mode test"; - break; - case KRMATERIAL_ALPHA_MODE_BLENDONESIDE: - stream << "\nalpha_mode blendoneside"; - break; - case KRMATERIAL_ALPHA_MODE_BLENDTWOSIDE: - stream << "\nalpha_mode blendtwoside"; - break; - } - stream << "\n# alpha_mode opaque, test, blendoneside, or blendtwoside"; - - stream << "\n"; - data.append(stream.str()); - - return true; -} - -void KRMaterial::setAmbientMap(std::string texture_name, Vector2 texture_scale, Vector2 texture_offset) { - m_ambientMap = texture_name; - m_ambientMapScale = texture_scale; - m_ambientMapOffset = texture_offset; -} - -void KRMaterial::setDiffuseMap(std::string texture_name, Vector2 texture_scale, Vector2 texture_offset) { - m_diffuseMap = texture_name; - m_diffuseMapScale = texture_scale; - m_diffuseMapOffset = texture_offset; -} - -void KRMaterial::setSpecularMap(std::string texture_name, Vector2 texture_scale, Vector2 texture_offset) { - m_specularMap = texture_name; - m_specularMapScale = texture_scale; - m_specularMapOffset = texture_offset; -} - -void KRMaterial::setNormalMap(std::string texture_name, Vector2 texture_scale, Vector2 texture_offset) { - m_normalMap = texture_name; - m_normalMapScale = texture_scale; - m_normalMapOffset = texture_offset; -} - -void KRMaterial::setReflectionMap(std::string texture_name, Vector2 texture_scale, Vector2 texture_offset) { - m_reflectionMap = texture_name; - m_reflectionMapScale = texture_scale; - m_reflectionMapOffset = texture_offset; -} - -void KRMaterial::setReflectionCube(std::string texture_name) { - m_reflectionCube = texture_name; -} - -void KRMaterial::setAlphaMode(KRMaterial::alpha_mode_type alpha_mode) { - m_alpha_mode = alpha_mode; -} - -KRMaterial::alpha_mode_type KRMaterial::getAlphaMode() { - return m_alpha_mode; -} - -void KRMaterial::setAmbient(const Vector3 &c) { - m_ambientColor = c; -} - -void KRMaterial::setDiffuse(const Vector3 &c) { - m_diffuseColor = c; -} - -void KRMaterial::setSpecular(const Vector3 &c) { - m_specularColor = c; -} - -void KRMaterial::setReflection(const Vector3 &c) { - m_reflectionColor = c; -} - -void KRMaterial::setTransparency(GLfloat a) { - if(a < 1.0f && m_alpha_mode == KRMaterial::KRMATERIAL_ALPHA_MODE_OPAQUE) { - setAlphaMode(KRMaterial::KRMATERIAL_ALPHA_MODE_BLENDONESIDE); - } - m_tr = a; -} - -void KRMaterial::setShininess(GLfloat s) { - m_ns = s; -} - -bool KRMaterial::isTransparent() { - return m_tr < 1.0 || m_alpha_mode == KRMATERIAL_ALPHA_MODE_BLENDONESIDE || m_alpha_mode == KRMATERIAL_ALPHA_MODE_BLENDTWOSIDE; -} - -void KRMaterial::preStream(float lodCoverage) -{ - getTextures(); - - if(m_pAmbientMap) { - m_pAmbientMap->resetPoolExpiry(lodCoverage, KRTexture::TEXTURE_USAGE_AMBIENT_MAP); - } - - if(m_pDiffuseMap) { - m_pDiffuseMap->resetPoolExpiry(lodCoverage, KRTexture::TEXTURE_USAGE_DIFFUSE_MAP); - } - - if(m_pNormalMap) { - m_pNormalMap->resetPoolExpiry(lodCoverage, KRTexture::TEXTURE_USAGE_NORMAL_MAP); - } - - if(m_pSpecularMap) { - m_pSpecularMap->resetPoolExpiry(lodCoverage, KRTexture::TEXTURE_USAGE_SPECULAR_MAP); - } - - if(m_pReflectionMap) { - m_pReflectionMap->resetPoolExpiry(lodCoverage, KRTexture::TEXTURE_USAGE_REFLECTION_MAP); - } - - if(m_pReflectionCube) { - m_pReflectionCube->resetPoolExpiry(lodCoverage, KRTexture::TEXTURE_USAGE_REFECTION_CUBE); - } -} - - -kraken_stream_level KRMaterial::getStreamLevel() -{ - kraken_stream_level stream_level = kraken_stream_level::STREAM_LEVEL_IN_HQ; - - getTextures(); - - if(m_pAmbientMap) { - stream_level = KRMIN(stream_level, m_pAmbientMap->getStreamLevel(KRTexture::TEXTURE_USAGE_AMBIENT_MAP)); - } - - if(m_pDiffuseMap) { - stream_level = KRMIN(stream_level, m_pDiffuseMap->getStreamLevel(KRTexture::TEXTURE_USAGE_DIFFUSE_MAP)); - } - - if(m_pNormalMap) { - stream_level = KRMIN(stream_level, m_pNormalMap->getStreamLevel(KRTexture::TEXTURE_USAGE_NORMAL_MAP)); - } - - if(m_pSpecularMap) { - stream_level = KRMIN(stream_level, m_pSpecularMap->getStreamLevel(KRTexture::TEXTURE_USAGE_SPECULAR_MAP)); - } - - if(m_pReflectionMap) { - stream_level = KRMIN(stream_level, m_pReflectionMap->getStreamLevel(KRTexture::TEXTURE_USAGE_REFLECTION_MAP)); - } - - if(m_pReflectionCube) { - stream_level = KRMIN(stream_level, m_pReflectionCube->getStreamLevel(KRTexture::TEXTURE_USAGE_REFECTION_CUBE)); - } - - return stream_level; -} - -void KRMaterial::getTextures() -{ - if(!m_pAmbientMap && m_ambientMap.size()) { - m_pAmbientMap = getContext().getTextureManager()->getTexture(m_ambientMap); - } - if(!m_pDiffuseMap && m_diffuseMap.size()) { - m_pDiffuseMap = getContext().getTextureManager()->getTexture(m_diffuseMap); - } - if(!m_pNormalMap && m_normalMap.size()) { - m_pNormalMap = getContext().getTextureManager()->getTexture(m_normalMap); - } - if(!m_pSpecularMap && m_specularMap.size()) { - m_pSpecularMap = getContext().getTextureManager()->getTexture(m_specularMap); - } - if(!m_pReflectionMap && m_reflectionMap.size()) { - m_pReflectionMap = getContext().getTextureManager()->getTexture(m_reflectionMap); - } - if(!m_pReflectionCube && m_reflectionCube.size()) { - m_pReflectionCube = getContext().getTextureManager()->getTextureCube(m_reflectionCube.c_str()); - } -} - -bool KRMaterial::bind(KRCamera *pCamera, std::vector &point_lights, std::vector &directional_lights, std::vector&spot_lights, const std::vector &bones, const std::vector &bind_poses, const KRViewport &viewport, const Matrix4 &matModel, KRTexture *pLightMap, KRNode::RenderPass renderPass, const Vector3 &rim_color, float rim_power, float lod_coverage) { - bool bLightMap = pLightMap && pCamera->settings.bEnableLightMap; - - getTextures(); - - Vector2 default_scale = Vector2::One(); - Vector2 default_offset = Vector2::Zero(); - - bool bHasReflection = m_reflectionColor != Vector3::Zero(); - bool bDiffuseMap = m_pDiffuseMap != NULL && pCamera->settings.bEnableDiffuseMap; - bool bNormalMap = m_pNormalMap != NULL && pCamera->settings.bEnableNormalMap; - bool bSpecMap = m_pSpecularMap != NULL && pCamera->settings.bEnableSpecMap; - bool bReflectionMap = m_pReflectionMap != NULL && pCamera->settings.bEnableReflectionMap && pCamera->settings.bEnableReflection && bHasReflection; - bool bReflectionCubeMap = m_pReflectionCube != NULL && pCamera->settings.bEnableReflection && bHasReflection; - bool bAlphaTest = (m_alpha_mode == KRMATERIAL_ALPHA_MODE_TEST) && bDiffuseMap; - bool bAlphaBlend = (m_alpha_mode == KRMATERIAL_ALPHA_MODE_BLENDONESIDE) || (m_alpha_mode == KRMATERIAL_ALPHA_MODE_BLENDTWOSIDE); - - KRShader *pShader = getContext().getShaderManager()->getShader("ObjectShader", pCamera, point_lights, directional_lights, spot_lights, bones.size(), bDiffuseMap, bNormalMap, bSpecMap, bReflectionMap, bReflectionCubeMap, bLightMap, m_diffuseMapScale != default_scale && bDiffuseMap, m_specularMapScale != default_scale && bSpecMap, m_normalMapScale != default_scale && bNormalMap, m_reflectionMapScale != default_scale && bReflectionMap, m_diffuseMapOffset != default_offset && bDiffuseMap, m_specularMapOffset != default_offset && bSpecMap, m_normalMapOffset != default_offset && bNormalMap, m_reflectionMapOffset != default_offset && bReflectionMap, bAlphaTest, bAlphaBlend, renderPass, rim_power != 0.0f); - - - Vector4 fade_color; - if(!getContext().getShaderManager()->selectShader(*pCamera, pShader, viewport, matModel, point_lights, directional_lights, spot_lights, 0, renderPass, rim_color, rim_power, fade_color)) { - return false; - } - - // Bind bones - if(pShader->m_uniforms[KRShader::KRENGINE_UNIFORM_BONE_TRANSFORMS] != -1) { - GLfloat bone_mats[256 * 16]; - GLfloat *bone_mat_component = bone_mats; - for(int bone_index=0; bone_index < bones.size(); bone_index++) { - KRBone *bone = bones[bone_index]; - -// Vector3 initialRotation = bone->getInitialLocalRotation(); -// Vector3 rotation = bone->getLocalRotation(); -// Vector3 initialTranslation = bone->getInitialLocalTranslation(); -// Vector3 translation = bone->getLocalTranslation(); -// Vector3 initialScale = bone->getInitialLocalScale(); -// Vector3 scale = bone->getLocalScale(); -// - //printf("%s - delta rotation: %.4f %.4f %.4f\n", bone->getName().c_str(), (rotation.x - initialRotation.x) * 180.0 / M_PI, (rotation.y - initialRotation.y) * 180.0 / M_PI, (rotation.z - initialRotation.z) * 180.0 / M_PI); - //printf("%s - delta translation: %.4f %.4f %.4f\n", bone->getName().c_str(), translation.x - initialTranslation.x, translation.y - initialTranslation.y, translation.z - initialTranslation.z); -// printf("%s - delta scale: %.4f %.4f %.4f\n", bone->getName().c_str(), scale.x - initialScale.x, scale.y - initialScale.y, scale.z - initialScale.z); - - Matrix4 skin_bone_bind_pose = bind_poses[bone_index]; - Matrix4 active_mat = bone->getActivePoseMatrix(); - Matrix4 inv_bind_mat = bone->getInverseBindPoseMatrix(); - Matrix4 inv_bind_mat2 = Matrix4::Invert(bind_poses[bone_index]); - Matrix4 t = (inv_bind_mat * active_mat); - Matrix4 t2 = inv_bind_mat2 * bone->getModelMatrix(); - for(int i=0; i < 16; i++) { - *bone_mat_component++ = t[i]; - } - } - if(pShader->m_uniforms[KRShader::KRENGINE_UNIFORM_BONE_TRANSFORMS] != -1) { - glUniformMatrix4fv(pShader->m_uniforms[KRShader::KRENGINE_UNIFORM_BONE_TRANSFORMS], bones.size(), GL_FALSE, bone_mats); - } - } - - - pShader->setUniform(KRShader::KRENGINE_UNIFORM_MATERIAL_AMBIENT, m_ambientColor + pCamera->settings.ambient_intensity); - - if(renderPass == KRNode::RENDER_PASS_FORWARD_OPAQUE) { - // We pre-multiply the light color with the material color in the forward renderer - pShader->setUniform(KRShader::KRENGINE_UNIFORM_MATERIAL_DIFFUSE, Vector3(m_diffuseColor.x * pCamera->settings.light_intensity.x, m_diffuseColor.y * pCamera->settings.light_intensity.y, m_diffuseColor.z * pCamera->settings.light_intensity.z)); - } else { - pShader->setUniform(KRShader::KRENGINE_UNIFORM_MATERIAL_DIFFUSE, m_diffuseColor); - } - - if(renderPass == KRNode::RENDER_PASS_FORWARD_OPAQUE) { - // We pre-multiply the light color with the material color in the forward renderer - pShader->setUniform(KRShader::KRENGINE_UNIFORM_MATERIAL_SPECULAR, Vector3(m_specularColor.x * pCamera->settings.light_intensity.x, m_specularColor.y * pCamera->settings.light_intensity.y, m_specularColor.z * pCamera->settings.light_intensity.z)); - } else { - pShader->setUniform(KRShader::KRENGINE_UNIFORM_MATERIAL_SPECULAR, m_specularColor); - } - - pShader->setUniform(KRShader::KRENGINE_UNIFORM_MATERIAL_SHININESS, m_ns); - pShader->setUniform(KRShader::KRENGINE_UNIFORM_MATERIAL_REFLECTION, m_reflectionColor); - pShader->setUniform(KRShader::KRENGINE_UNIFORM_DIFFUSETEXTURE_SCALE, m_diffuseMapScale); - pShader->setUniform(KRShader::KRENGINE_UNIFORM_SPECULARTEXTURE_SCALE, m_specularMapScale); - pShader->setUniform(KRShader::KRENGINE_UNIFORM_REFLECTIONTEXTURE_SCALE, m_reflectionMapScale); - pShader->setUniform(KRShader::KRENGINE_UNIFORM_NORMALTEXTURE_SCALE, m_normalMapScale); - pShader->setUniform(KRShader::KRENGINE_UNIFORM_DIFFUSETEXTURE_OFFSET, m_diffuseMapOffset); - pShader->setUniform(KRShader::KRENGINE_UNIFORM_SPECULARTEXTURE_OFFSET, m_specularMapOffset); - pShader->setUniform(KRShader::KRENGINE_UNIFORM_REFLECTIONTEXTURE_OFFSET, m_reflectionMapOffset); - pShader->setUniform(KRShader::KRENGINE_UNIFORM_NORMALTEXTURE_OFFSET, m_normalMapOffset); - - pShader->setUniform(KRShader::KRENGINE_UNIFORM_MATERIAL_ALPHA, m_tr); - - if(bDiffuseMap) { - m_pContext->getTextureManager()->selectTexture(0, m_pDiffuseMap, lod_coverage, KRTexture::TEXTURE_USAGE_DIFFUSE_MAP); - } - - if(bSpecMap) { - m_pContext->getTextureManager()->selectTexture(1, m_pSpecularMap, lod_coverage, KRTexture::TEXTURE_USAGE_SPECULAR_MAP); - } - - if(bNormalMap) { - m_pContext->getTextureManager()->selectTexture(2, m_pNormalMap, lod_coverage, KRTexture::TEXTURE_USAGE_NORMAL_MAP); - } - - if(bReflectionCubeMap && (renderPass == KRNode::RENDER_PASS_FORWARD_OPAQUE || renderPass == KRNode::RENDER_PASS_FORWARD_TRANSPARENT || renderPass == KRNode::RENDER_PASS_DEFERRED_OPAQUE)) { - m_pContext->getTextureManager()->selectTexture(4, m_pReflectionCube, lod_coverage, KRTexture::TEXTURE_USAGE_REFECTION_CUBE); - } - - if(bReflectionMap && (renderPass == KRNode::RENDER_PASS_FORWARD_OPAQUE || renderPass == KRNode::RENDER_PASS_FORWARD_TRANSPARENT || renderPass == KRNode::RENDER_PASS_DEFERRED_OPAQUE)) { - // GL_TEXTURE7 is used for reading the depth buffer in gBuffer pass 2 and re-used for the reflection map in gBuffer Pass 3 and in forward rendering - m_pContext->getTextureManager()->selectTexture(7, m_pReflectionMap, lod_coverage, KRTexture::TEXTURE_USAGE_REFLECTION_MAP); - } - - - return true; -} - -const std::string &KRMaterial::getName() const -{ - return m_name; -} - +// +// KRMaterial.cpp +// KREngine +// +// Copyright 2012 Kearwood Gilbert. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other materials +// provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// The views and conclusions contained in the software and documentation are those of the +// authors and should not be interpreted as representing official policies, either expressed +// or implied, of Kearwood Gilbert. +// + +#include "KREngine-common.h" + +#include "KRMaterial.h" +#include "KRTextureManager.h" + +#include "KRContext.h" + +KRMaterial::KRMaterial(KRContext &context, const char *szName) : KRResource(context, szName) { + m_name = szName; + m_pAmbientMap = NULL; + m_pDiffuseMap = NULL; + m_pSpecularMap = NULL; + m_pNormalMap = NULL; + m_pReflectionMap = NULL; + m_pReflectionCube = NULL; + m_ambientColor = Vector3::Zero(); + m_diffuseColor = Vector3::One(); + m_specularColor = Vector3::One(); + m_reflectionColor = Vector3::Zero(); + m_tr = (GLfloat)1.0f; + m_ns = (GLfloat)0.0f; + m_ambientMap = ""; + m_diffuseMap = ""; + m_specularMap = ""; + m_normalMap = ""; + m_reflectionMap = ""; + m_reflectionCube = ""; + m_ambientMapOffset = Vector2::Create(0.0f, 0.0f); + m_specularMapOffset = Vector2::Create(0.0f, 0.0f); + m_diffuseMapOffset = Vector2::Create(0.0f, 0.0f); + m_ambientMapScale = Vector2::Create(1.0f, 1.0f); + m_specularMapScale = Vector2::Create(1.0f, 1.0f); + m_diffuseMapScale = Vector2::Create(1.0f, 1.0f); + m_reflectionMapOffset = Vector2::Create(0.0f, 0.0f); + m_reflectionMapScale = Vector2::Create(1.0f, 1.0f); + m_alpha_mode = KRMATERIAL_ALPHA_MODE_OPAQUE; +} + +KRMaterial::~KRMaterial() { + +} + +std::string KRMaterial::getExtension() { + return "mtl"; +} + +bool KRMaterial::needsVertexTangents() +{ + return m_normalMap.size() > 0; +} + +bool KRMaterial::save(KRDataBlock &data) { + std::stringstream stream; + stream.precision(std::numeric_limits::digits10); + stream.setf(std::ios::fixed,std::ios::floatfield); + + stream << "newmtl " << m_name; + stream << "\nka " << m_ambientColor.x << " " << m_ambientColor.y << " " << m_ambientColor.z; + stream << "\nkd " << m_diffuseColor.x << " " << m_diffuseColor.y << " " << m_diffuseColor.z; + stream << "\nks " << m_specularColor.x << " " << m_specularColor.y << " " << m_specularColor.z; + stream << "\nkr " << m_reflectionColor.x << " " << m_reflectionColor.y << " " << m_reflectionColor.z; + stream << "\nTr " << m_tr; + stream << "\nNs " << m_ns; + if(m_ambientMap.size()) { + stream << "\nmap_Ka " << m_ambientMap << ".pvr -s " << m_ambientMapScale.x << " " << m_ambientMapScale.y << " -o " << m_ambientMapOffset.x << " " << m_ambientMapOffset.y; + } else { + stream << "\n# map_Ka filename.pvr -s 1.0 1.0 -o 0.0 0.0"; + } + if(m_diffuseMap.size()) { + stream << "\nmap_Kd " << m_diffuseMap << ".pvr -s " << m_diffuseMapScale.x << " " << m_diffuseMapScale.y << " -o " << m_diffuseMapOffset.x << " " << m_diffuseMapOffset.y; + } else { + stream << "\n# map_Kd filename.pvr -s 1.0 1.0 -o 0.0 0.0"; + } + if(m_specularMap.size()) { + stream << "\nmap_Ks " << m_specularMap << ".pvr -s " << m_specularMapScale.x << " " << m_specularMapScale.y << " -o " << m_specularMapOffset.x << " " << m_specularMapOffset.y << "\n"; + } else { + stream << "\n# map_Ks filename.pvr -s 1.0 1.0 -o 0.0 0.0"; + } + if(m_normalMap.size()) { + stream << "\nmap_Normal " << m_normalMap << ".pvr -s " << m_normalMapScale.x << " " << m_normalMapScale.y << " -o " << m_normalMapOffset.x << " " << m_normalMapOffset.y; + } else { + stream << "\n# map_Normal filename.pvr -s 1.0 1.0 -o 0.0 0.0"; + } + if(m_reflectionMap.size()) { + stream << "\nmap_Reflection " << m_reflectionMap << ".pvr -s " << m_reflectionMapScale.x << " " << m_reflectionMapScale.y << " -o " << m_reflectionMapOffset.x << " " << m_reflectionMapOffset.y; + } else { + stream << "\n# map_Reflection filename.pvr -s 1.0 1.0 -o 0.0 0.0"; + } + if(m_reflectionCube.size()) { + stream << "\nmap_ReflectionCube " << m_reflectionCube << ".pvr"; + } else { + stream << "\n# map_ReflectionCube cubemapname"; + } + switch(m_alpha_mode) { + case KRMATERIAL_ALPHA_MODE_OPAQUE: + stream << "\nalpha_mode opaque"; + break; + case KRMATERIAL_ALPHA_MODE_TEST: + stream << "\nalpha_mode test"; + break; + case KRMATERIAL_ALPHA_MODE_BLENDONESIDE: + stream << "\nalpha_mode blendoneside"; + break; + case KRMATERIAL_ALPHA_MODE_BLENDTWOSIDE: + stream << "\nalpha_mode blendtwoside"; + break; + } + stream << "\n# alpha_mode opaque, test, blendoneside, or blendtwoside"; + + stream << "\n"; + data.append(stream.str()); + + return true; +} + +void KRMaterial::setAmbientMap(std::string texture_name, Vector2 texture_scale, Vector2 texture_offset) { + m_ambientMap = texture_name; + m_ambientMapScale = texture_scale; + m_ambientMapOffset = texture_offset; +} + +void KRMaterial::setDiffuseMap(std::string texture_name, Vector2 texture_scale, Vector2 texture_offset) { + m_diffuseMap = texture_name; + m_diffuseMapScale = texture_scale; + m_diffuseMapOffset = texture_offset; +} + +void KRMaterial::setSpecularMap(std::string texture_name, Vector2 texture_scale, Vector2 texture_offset) { + m_specularMap = texture_name; + m_specularMapScale = texture_scale; + m_specularMapOffset = texture_offset; +} + +void KRMaterial::setNormalMap(std::string texture_name, Vector2 texture_scale, Vector2 texture_offset) { + m_normalMap = texture_name; + m_normalMapScale = texture_scale; + m_normalMapOffset = texture_offset; +} + +void KRMaterial::setReflectionMap(std::string texture_name, Vector2 texture_scale, Vector2 texture_offset) { + m_reflectionMap = texture_name; + m_reflectionMapScale = texture_scale; + m_reflectionMapOffset = texture_offset; +} + +void KRMaterial::setReflectionCube(std::string texture_name) { + m_reflectionCube = texture_name; +} + +void KRMaterial::setAlphaMode(KRMaterial::alpha_mode_type alpha_mode) { + m_alpha_mode = alpha_mode; +} + +KRMaterial::alpha_mode_type KRMaterial::getAlphaMode() { + return m_alpha_mode; +} + +void KRMaterial::setAmbient(const Vector3 &c) { + m_ambientColor = c; +} + +void KRMaterial::setDiffuse(const Vector3 &c) { + m_diffuseColor = c; +} + +void KRMaterial::setSpecular(const Vector3 &c) { + m_specularColor = c; +} + +void KRMaterial::setReflection(const Vector3 &c) { + m_reflectionColor = c; +} + +void KRMaterial::setTransparency(GLfloat a) { + if(a < 1.0f && m_alpha_mode == KRMaterial::KRMATERIAL_ALPHA_MODE_OPAQUE) { + setAlphaMode(KRMaterial::KRMATERIAL_ALPHA_MODE_BLENDONESIDE); + } + m_tr = a; +} + +void KRMaterial::setShininess(GLfloat s) { + m_ns = s; +} + +bool KRMaterial::isTransparent() { + return m_tr < 1.0 || m_alpha_mode == KRMATERIAL_ALPHA_MODE_BLENDONESIDE || m_alpha_mode == KRMATERIAL_ALPHA_MODE_BLENDTWOSIDE; +} + +void KRMaterial::preStream(float lodCoverage) +{ + getTextures(); + + if(m_pAmbientMap) { + m_pAmbientMap->resetPoolExpiry(lodCoverage, KRTexture::TEXTURE_USAGE_AMBIENT_MAP); + } + + if(m_pDiffuseMap) { + m_pDiffuseMap->resetPoolExpiry(lodCoverage, KRTexture::TEXTURE_USAGE_DIFFUSE_MAP); + } + + if(m_pNormalMap) { + m_pNormalMap->resetPoolExpiry(lodCoverage, KRTexture::TEXTURE_USAGE_NORMAL_MAP); + } + + if(m_pSpecularMap) { + m_pSpecularMap->resetPoolExpiry(lodCoverage, KRTexture::TEXTURE_USAGE_SPECULAR_MAP); + } + + if(m_pReflectionMap) { + m_pReflectionMap->resetPoolExpiry(lodCoverage, KRTexture::TEXTURE_USAGE_REFLECTION_MAP); + } + + if(m_pReflectionCube) { + m_pReflectionCube->resetPoolExpiry(lodCoverage, KRTexture::TEXTURE_USAGE_REFECTION_CUBE); + } +} + + +kraken_stream_level KRMaterial::getStreamLevel() +{ + kraken_stream_level stream_level = kraken_stream_level::STREAM_LEVEL_IN_HQ; + + getTextures(); + + if(m_pAmbientMap) { + stream_level = KRMIN(stream_level, m_pAmbientMap->getStreamLevel(KRTexture::TEXTURE_USAGE_AMBIENT_MAP)); + } + + if(m_pDiffuseMap) { + stream_level = KRMIN(stream_level, m_pDiffuseMap->getStreamLevel(KRTexture::TEXTURE_USAGE_DIFFUSE_MAP)); + } + + if(m_pNormalMap) { + stream_level = KRMIN(stream_level, m_pNormalMap->getStreamLevel(KRTexture::TEXTURE_USAGE_NORMAL_MAP)); + } + + if(m_pSpecularMap) { + stream_level = KRMIN(stream_level, m_pSpecularMap->getStreamLevel(KRTexture::TEXTURE_USAGE_SPECULAR_MAP)); + } + + if(m_pReflectionMap) { + stream_level = KRMIN(stream_level, m_pReflectionMap->getStreamLevel(KRTexture::TEXTURE_USAGE_REFLECTION_MAP)); + } + + if(m_pReflectionCube) { + stream_level = KRMIN(stream_level, m_pReflectionCube->getStreamLevel(KRTexture::TEXTURE_USAGE_REFECTION_CUBE)); + } + + return stream_level; +} + +void KRMaterial::getTextures() +{ + if(!m_pAmbientMap && m_ambientMap.size()) { + m_pAmbientMap = getContext().getTextureManager()->getTexture(m_ambientMap); + } + if(!m_pDiffuseMap && m_diffuseMap.size()) { + m_pDiffuseMap = getContext().getTextureManager()->getTexture(m_diffuseMap); + } + if(!m_pNormalMap && m_normalMap.size()) { + m_pNormalMap = getContext().getTextureManager()->getTexture(m_normalMap); + } + if(!m_pSpecularMap && m_specularMap.size()) { + m_pSpecularMap = getContext().getTextureManager()->getTexture(m_specularMap); + } + if(!m_pReflectionMap && m_reflectionMap.size()) { + m_pReflectionMap = getContext().getTextureManager()->getTexture(m_reflectionMap); + } + if(!m_pReflectionCube && m_reflectionCube.size()) { + m_pReflectionCube = getContext().getTextureManager()->getTextureCube(m_reflectionCube.c_str()); + } +} + +bool KRMaterial::bind(KRCamera *pCamera, std::vector &point_lights, std::vector &directional_lights, std::vector&spot_lights, const std::vector &bones, const std::vector &bind_poses, const KRViewport &viewport, const Matrix4 &matModel, KRTexture *pLightMap, KRNode::RenderPass renderPass, const Vector3 &rim_color, float rim_power, float lod_coverage) { + bool bLightMap = pLightMap && pCamera->settings.bEnableLightMap; + + getTextures(); + + Vector2 default_scale = Vector2::One(); + Vector2 default_offset = Vector2::Zero(); + + bool bHasReflection = m_reflectionColor != Vector3::Zero(); + bool bDiffuseMap = m_pDiffuseMap != NULL && pCamera->settings.bEnableDiffuseMap; + bool bNormalMap = m_pNormalMap != NULL && pCamera->settings.bEnableNormalMap; + bool bSpecMap = m_pSpecularMap != NULL && pCamera->settings.bEnableSpecMap; + bool bReflectionMap = m_pReflectionMap != NULL && pCamera->settings.bEnableReflectionMap && pCamera->settings.bEnableReflection && bHasReflection; + bool bReflectionCubeMap = m_pReflectionCube != NULL && pCamera->settings.bEnableReflection && bHasReflection; + bool bAlphaTest = (m_alpha_mode == KRMATERIAL_ALPHA_MODE_TEST) && bDiffuseMap; + bool bAlphaBlend = (m_alpha_mode == KRMATERIAL_ALPHA_MODE_BLENDONESIDE) || (m_alpha_mode == KRMATERIAL_ALPHA_MODE_BLENDTWOSIDE); + + KRShader *pShader = getContext().getShaderManager()->getShader("ObjectShader", pCamera, point_lights, directional_lights, spot_lights, bones.size(), bDiffuseMap, bNormalMap, bSpecMap, bReflectionMap, bReflectionCubeMap, bLightMap, m_diffuseMapScale != default_scale && bDiffuseMap, m_specularMapScale != default_scale && bSpecMap, m_normalMapScale != default_scale && bNormalMap, m_reflectionMapScale != default_scale && bReflectionMap, m_diffuseMapOffset != default_offset && bDiffuseMap, m_specularMapOffset != default_offset && bSpecMap, m_normalMapOffset != default_offset && bNormalMap, m_reflectionMapOffset != default_offset && bReflectionMap, bAlphaTest, bAlphaBlend, renderPass, rim_power != 0.0f); + + + Vector4 fade_color; + if(!getContext().getShaderManager()->selectShader(*pCamera, pShader, viewport, matModel, point_lights, directional_lights, spot_lights, 0, renderPass, rim_color, rim_power, fade_color)) { + return false; + } + + // Bind bones + if(pShader->m_uniforms[KRShader::KRENGINE_UNIFORM_BONE_TRANSFORMS] != -1) { + GLfloat bone_mats[256 * 16]; + GLfloat *bone_mat_component = bone_mats; + for(int bone_index=0; bone_index < bones.size(); bone_index++) { + KRBone *bone = bones[bone_index]; + +// Vector3 initialRotation = bone->getInitialLocalRotation(); +// Vector3 rotation = bone->getLocalRotation(); +// Vector3 initialTranslation = bone->getInitialLocalTranslation(); +// Vector3 translation = bone->getLocalTranslation(); +// Vector3 initialScale = bone->getInitialLocalScale(); +// Vector3 scale = bone->getLocalScale(); +// + //printf("%s - delta rotation: %.4f %.4f %.4f\n", bone->getName().c_str(), (rotation.x - initialRotation.x) * 180.0 / M_PI, (rotation.y - initialRotation.y) * 180.0 / M_PI, (rotation.z - initialRotation.z) * 180.0 / M_PI); + //printf("%s - delta translation: %.4f %.4f %.4f\n", bone->getName().c_str(), translation.x - initialTranslation.x, translation.y - initialTranslation.y, translation.z - initialTranslation.z); +// printf("%s - delta scale: %.4f %.4f %.4f\n", bone->getName().c_str(), scale.x - initialScale.x, scale.y - initialScale.y, scale.z - initialScale.z); + + Matrix4 skin_bone_bind_pose = bind_poses[bone_index]; + Matrix4 active_mat = bone->getActivePoseMatrix(); + Matrix4 inv_bind_mat = bone->getInverseBindPoseMatrix(); + Matrix4 inv_bind_mat2 = Matrix4::Invert(bind_poses[bone_index]); + Matrix4 t = (inv_bind_mat * active_mat); + Matrix4 t2 = inv_bind_mat2 * bone->getModelMatrix(); + for(int i=0; i < 16; i++) { + *bone_mat_component++ = t[i]; + } + } + if(pShader->m_uniforms[KRShader::KRENGINE_UNIFORM_BONE_TRANSFORMS] != -1) { + glUniformMatrix4fv(pShader->m_uniforms[KRShader::KRENGINE_UNIFORM_BONE_TRANSFORMS], bones.size(), GL_FALSE, bone_mats); + } + } + + + pShader->setUniform(KRShader::KRENGINE_UNIFORM_MATERIAL_AMBIENT, m_ambientColor + pCamera->settings.ambient_intensity); + + if(renderPass == KRNode::RENDER_PASS_FORWARD_OPAQUE) { + // We pre-multiply the light color with the material color in the forward renderer + pShader->setUniform(KRShader::KRENGINE_UNIFORM_MATERIAL_DIFFUSE, Vector3::Create(m_diffuseColor.x * pCamera->settings.light_intensity.x, m_diffuseColor.y * pCamera->settings.light_intensity.y, m_diffuseColor.z * pCamera->settings.light_intensity.z)); + } else { + pShader->setUniform(KRShader::KRENGINE_UNIFORM_MATERIAL_DIFFUSE, m_diffuseColor); + } + + if(renderPass == KRNode::RENDER_PASS_FORWARD_OPAQUE) { + // We pre-multiply the light color with the material color in the forward renderer + pShader->setUniform(KRShader::KRENGINE_UNIFORM_MATERIAL_SPECULAR, Vector3::Create(m_specularColor.x * pCamera->settings.light_intensity.x, m_specularColor.y * pCamera->settings.light_intensity.y, m_specularColor.z * pCamera->settings.light_intensity.z)); + } else { + pShader->setUniform(KRShader::KRENGINE_UNIFORM_MATERIAL_SPECULAR, m_specularColor); + } + + pShader->setUniform(KRShader::KRENGINE_UNIFORM_MATERIAL_SHININESS, m_ns); + pShader->setUniform(KRShader::KRENGINE_UNIFORM_MATERIAL_REFLECTION, m_reflectionColor); + pShader->setUniform(KRShader::KRENGINE_UNIFORM_DIFFUSETEXTURE_SCALE, m_diffuseMapScale); + pShader->setUniform(KRShader::KRENGINE_UNIFORM_SPECULARTEXTURE_SCALE, m_specularMapScale); + pShader->setUniform(KRShader::KRENGINE_UNIFORM_REFLECTIONTEXTURE_SCALE, m_reflectionMapScale); + pShader->setUniform(KRShader::KRENGINE_UNIFORM_NORMALTEXTURE_SCALE, m_normalMapScale); + pShader->setUniform(KRShader::KRENGINE_UNIFORM_DIFFUSETEXTURE_OFFSET, m_diffuseMapOffset); + pShader->setUniform(KRShader::KRENGINE_UNIFORM_SPECULARTEXTURE_OFFSET, m_specularMapOffset); + pShader->setUniform(KRShader::KRENGINE_UNIFORM_REFLECTIONTEXTURE_OFFSET, m_reflectionMapOffset); + pShader->setUniform(KRShader::KRENGINE_UNIFORM_NORMALTEXTURE_OFFSET, m_normalMapOffset); + + pShader->setUniform(KRShader::KRENGINE_UNIFORM_MATERIAL_ALPHA, m_tr); + + if(bDiffuseMap) { + m_pContext->getTextureManager()->selectTexture(0, m_pDiffuseMap, lod_coverage, KRTexture::TEXTURE_USAGE_DIFFUSE_MAP); + } + + if(bSpecMap) { + m_pContext->getTextureManager()->selectTexture(1, m_pSpecularMap, lod_coverage, KRTexture::TEXTURE_USAGE_SPECULAR_MAP); + } + + if(bNormalMap) { + m_pContext->getTextureManager()->selectTexture(2, m_pNormalMap, lod_coverage, KRTexture::TEXTURE_USAGE_NORMAL_MAP); + } + + if(bReflectionCubeMap && (renderPass == KRNode::RENDER_PASS_FORWARD_OPAQUE || renderPass == KRNode::RENDER_PASS_FORWARD_TRANSPARENT || renderPass == KRNode::RENDER_PASS_DEFERRED_OPAQUE)) { + m_pContext->getTextureManager()->selectTexture(4, m_pReflectionCube, lod_coverage, KRTexture::TEXTURE_USAGE_REFECTION_CUBE); + } + + if(bReflectionMap && (renderPass == KRNode::RENDER_PASS_FORWARD_OPAQUE || renderPass == KRNode::RENDER_PASS_FORWARD_TRANSPARENT || renderPass == KRNode::RENDER_PASS_DEFERRED_OPAQUE)) { + // GL_TEXTURE7 is used for reading the depth buffer in gBuffer pass 2 and re-used for the reflection map in gBuffer Pass 3 and in forward rendering + m_pContext->getTextureManager()->selectTexture(7, m_pReflectionMap, lod_coverage, KRTexture::TEXTURE_USAGE_REFLECTION_MAP); + } + + + return true; +} + +const std::string &KRMaterial::getName() const +{ + return m_name; +} + diff --git a/kraken/KRMaterialManager.cpp b/kraken/KRMaterialManager.cpp index 944f783..a7cb06f 100755 --- a/kraken/KRMaterialManager.cpp +++ b/kraken/KRMaterialManager.cpp @@ -1,288 +1,288 @@ -// -// KRMaterialManager.cpp -// KREngine -// -// Copyright 2012 Kearwood Gilbert. All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, are -// permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of -// conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list -// of conditions and the following disclaimer in the documentation and/or other materials -// provided with the distribution. -// -// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED -// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR -// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// The views and conclusions contained in the software and documentation are those of the -// authors and should not be interpreted as representing official policies, either expressed -// or implied, of Kearwood Gilbert. -// - -#include "KREngine-common.h" -#include "KRMaterialManager.h" - - -KRMaterialManager::KRMaterialManager(KRContext &context, KRTextureManager *pTextureManager, KRShaderManager *pShaderManager) : KRContextObject(context) -{ - m_pTextureManager = pTextureManager; - m_pShaderManager = pShaderManager; -} - -KRMaterialManager::~KRMaterialManager() { - -} - - -unordered_map &KRMaterialManager::getMaterials() -{ - return m_materials; -} - -void KRMaterialManager::configure(bool blend_enable, GLenum blend_src, GLenum blend_dest, bool depth_test_enable, GLenum depth_func, bool depth_write_enable) { - if(blend_enable) { - GLDEBUG(glEnable(GL_BLEND)); - GLDEBUG(glBlendFunc(blend_src, blend_dest)); - } else { - GLDEBUG(glDisable(GL_BLEND)); - } - - if(depth_test_enable) { - GLDEBUG(glEnable(GL_DEPTH_TEST)); - GLDEBUG(glDepthFunc(depth_func)); - } else { - GLDEBUG(glDisable(GL_DEPTH_TEST)); - } - - if(depth_write_enable) { - GLDEBUG(glDepthMask(GL_TRUE)); - } else { - GLDEBUG(glDepthMask(GL_FALSE)); - } -} - - -KRMaterial *KRMaterialManager::getMaterial(const std::string &name) { - std::string lowerName = name; - std::transform(lowerName.begin(), lowerName.end(), - lowerName.begin(), ::tolower); - - - unordered_map::iterator itr = m_materials.find(lowerName); - if(itr == m_materials.end()) { - KRContext::Log(KRContext::LOG_LEVEL_WARNING, "Material not found: %s", name.c_str()); - // Not found - return NULL; - } else { - return (*itr).second; - } -} - -void KRMaterialManager::add(KRMaterial *new_material) { - // FINDME, TODO - Potential memory leak if multiple materials with the same name are added - std::string lowerName = new_material->getName(); - std::transform(lowerName.begin(), lowerName.end(), - lowerName.begin(), ::tolower); - - m_materials[lowerName] = new_material; -} - -bool KRMaterialManager::load(const char *szName, KRDataBlock *data) { - KRMaterial *pMaterial = NULL; - char szSymbol[16][256]; - data->lock(); - - char *pScan = (char *)data->getStart(); - char *pEnd = (char *)data->getEnd(); - while(pScan < pEnd) { - - // Scan through whitespace - while(pScan < pEnd && (*pScan == ' ' || *pScan == '\t' || *pScan == '\r' || *pScan == '\n')) { - pScan++; - } - - if(*pScan == '#') { - // Line is a comment line - - // Scan to the end of the line - while(pScan < pEnd && *pScan != '\r' && *pScan != '\n') { - pScan++; - } - } else { - int cSymbols = 0; - while(pScan < pEnd && *pScan != '\n' && *pScan != '\r') { - - char *pDest = szSymbol[cSymbols++]; - while(pScan < pEnd && *pScan != ' ' && *pScan != '\n' && *pScan != '\r') { - if(*pScan >= 'A' && *pScan <= 'Z') { - *pDest++ = *pScan++ + 'a' - 'A'; // convert to lower case for case sensitve comparison later - } else { - *pDest++ = *pScan++; - } - } - *pDest = '\0'; - - // Scan through whitespace, but don't advance to next line - while(pScan < pEnd && (*pScan == ' ' || *pScan == '\t')) { - pScan++; - } - } - - if(cSymbols > 0) { - - if(strcmp(szSymbol[0], "newmtl") == 0 && cSymbols >= 2) { - - pMaterial = new KRMaterial(*m_pContext, szSymbol[1]); - m_materials[szSymbol[1]] = pMaterial; - } - if(pMaterial != NULL) { - if(strcmp(szSymbol[0], "alpha_mode") == 0) { - if(cSymbols == 2) { - if(strcmp(szSymbol[1], "test") == 0) { - pMaterial->setAlphaMode(KRMaterial::KRMATERIAL_ALPHA_MODE_TEST); - } else if(strcmp(szSymbol[1], "blendoneside") == 0) { - pMaterial->setAlphaMode(KRMaterial::KRMATERIAL_ALPHA_MODE_BLENDONESIDE); - } else if(strcmp(szSymbol[1], "blendtwoside") == 0) { - pMaterial->setAlphaMode(KRMaterial::KRMATERIAL_ALPHA_MODE_BLENDTWOSIDE); - } else { - pMaterial->setAlphaMode(KRMaterial::KRMATERIAL_ALPHA_MODE_OPAQUE); - } - } - } else if(strcmp(szSymbol[0], "ka") == 0) { - char *pScan2 = szSymbol[1]; - float r = strtof(pScan2, &pScan2); - if(cSymbols == 2) { - pMaterial->setAmbient(Vector3(r, r, r)); - } else if(cSymbols == 4) { - pScan2 = szSymbol[2]; - float g = strtof(pScan2, &pScan2); - pScan2 = szSymbol[3]; - float b = strtof(pScan2, &pScan2); - pMaterial->setAmbient(Vector3(r, g, b)); - } - } else if(strcmp(szSymbol[0], "kd") == 0) { - char *pScan2 = szSymbol[1]; - float r = strtof(pScan2, &pScan2); - if(cSymbols == 2) { - pMaterial->setDiffuse(Vector3(r, r, r)); - } else if(cSymbols == 4) { - pScan2 = szSymbol[2]; - float g = strtof(pScan2, &pScan2); - pScan2 = szSymbol[3]; - float b = strtof(pScan2, &pScan2); - pMaterial->setDiffuse(Vector3(r, g, b)); - } - } else if(strcmp(szSymbol[0], "ks") == 0) { - char *pScan2 = szSymbol[1]; - float r = strtof(pScan2, &pScan2); - if(cSymbols == 2) { - pMaterial->setSpecular(Vector3(r, r, r)); - } else if(cSymbols == 4) { - pScan2 = szSymbol[2]; - float g = strtof(pScan2, &pScan2); - pScan2 = szSymbol[3]; - float b = strtof(pScan2, &pScan2); - pMaterial->setSpecular(Vector3(r, g, b)); - } - } else if(strcmp(szSymbol[0], "kr") == 0) { - char *pScan2 = szSymbol[1]; - float r = strtof(pScan2, &pScan2); - if(cSymbols == 2) { - pMaterial->setReflection(Vector3(r, r, r)); - } else if(cSymbols == 4) { - pScan2 = szSymbol[2]; - float g = strtof(pScan2, &pScan2); - pScan2 = szSymbol[3]; - float b = strtof(pScan2, &pScan2); - pMaterial->setReflection(Vector3(r, g, b)); - } - } else if(strcmp(szSymbol[0], "tr") == 0) { - char *pScan2 = szSymbol[1]; - float a = strtof(pScan2, &pScan2); - pMaterial->setTransparency(a); - } else if(strcmp(szSymbol[0], "ns") == 0) { - char *pScan2 = szSymbol[1]; - float a = strtof(pScan2, &pScan2); - pMaterial->setShininess(a); - } else if(strncmp(szSymbol[0], "map", 3) == 0) { - // Truncate file extension - char *pScan2 = szSymbol[1]; - char *pLastPeriod = NULL; - while(*pScan2 != '\0') { - if(*pScan2 == '.') { - pLastPeriod = pScan2; - } - pScan2++; - } - if(pLastPeriod) { - *pLastPeriod = '\0'; - } - - Vector2 texture_scale = Vector2(1.0f, 1.0f); - Vector2 texture_offset = Vector2(0.0f, 0.0f); - - int iScanSymbol = 2; - int iScaleParam = -1; - int iOffsetParam = -1; - while(iScanSymbol < cSymbols) { - if(strcmp(szSymbol[iScanSymbol], "-s") == 0) { - // Scale - iScaleParam = 0; - iOffsetParam = -1; - } else if(strcmp(szSymbol[iScanSymbol], "-o") == 0) { - // Offset - iOffsetParam = 0; - iScaleParam = -1; - } else { - char *pScan3 = szSymbol[iScanSymbol]; - float v = strtof(pScan3, &pScan3); - if(iScaleParam == 0) { - texture_scale.x = v; - iScaleParam++; - } else if(iScaleParam == 1) { - texture_scale.y = v; - iScaleParam++; - } else if(iOffsetParam == 0) { - texture_offset.x = v; - iOffsetParam++; - } else if(iOffsetParam == 1) { - texture_offset.y = v; - iOffsetParam++; - } - } - iScanSymbol++; - } - - if(strcmp(szSymbol[0], "map_ka") == 0) { - pMaterial->setAmbientMap(szSymbol[1], texture_scale, texture_offset); - } else if(strcmp(szSymbol[0], "map_kd") == 0) { - pMaterial->setDiffuseMap(szSymbol[1], texture_scale, texture_offset); - } else if(strcmp(szSymbol[0], "map_ks") == 0) { - pMaterial->setSpecularMap(szSymbol[1], texture_scale, texture_offset); - } else if(strcmp(szSymbol[0], "map_normal") == 0) { - pMaterial->setNormalMap(szSymbol[1], texture_scale, texture_offset); - } else if(strcmp(szSymbol[0], "map_reflection") == 0) { - pMaterial->setReflectionMap(szSymbol[1], texture_scale, texture_offset); - } else if(strcmp(szSymbol[0], "map_reflectioncube") == 0) { - pMaterial->setReflectionCube(szSymbol[1]); - } - } - } - } - } - - } - data->unlock(); - delete data; - return true; -} +// +// KRMaterialManager.cpp +// KREngine +// +// Copyright 2012 Kearwood Gilbert. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other materials +// provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// The views and conclusions contained in the software and documentation are those of the +// authors and should not be interpreted as representing official policies, either expressed +// or implied, of Kearwood Gilbert. +// + +#include "KREngine-common.h" +#include "KRMaterialManager.h" + + +KRMaterialManager::KRMaterialManager(KRContext &context, KRTextureManager *pTextureManager, KRShaderManager *pShaderManager) : KRContextObject(context) +{ + m_pTextureManager = pTextureManager; + m_pShaderManager = pShaderManager; +} + +KRMaterialManager::~KRMaterialManager() { + +} + + +unordered_map &KRMaterialManager::getMaterials() +{ + return m_materials; +} + +void KRMaterialManager::configure(bool blend_enable, GLenum blend_src, GLenum blend_dest, bool depth_test_enable, GLenum depth_func, bool depth_write_enable) { + if(blend_enable) { + GLDEBUG(glEnable(GL_BLEND)); + GLDEBUG(glBlendFunc(blend_src, blend_dest)); + } else { + GLDEBUG(glDisable(GL_BLEND)); + } + + if(depth_test_enable) { + GLDEBUG(glEnable(GL_DEPTH_TEST)); + GLDEBUG(glDepthFunc(depth_func)); + } else { + GLDEBUG(glDisable(GL_DEPTH_TEST)); + } + + if(depth_write_enable) { + GLDEBUG(glDepthMask(GL_TRUE)); + } else { + GLDEBUG(glDepthMask(GL_FALSE)); + } +} + + +KRMaterial *KRMaterialManager::getMaterial(const std::string &name) { + std::string lowerName = name; + std::transform(lowerName.begin(), lowerName.end(), + lowerName.begin(), ::tolower); + + + unordered_map::iterator itr = m_materials.find(lowerName); + if(itr == m_materials.end()) { + KRContext::Log(KRContext::LOG_LEVEL_WARNING, "Material not found: %s", name.c_str()); + // Not found + return NULL; + } else { + return (*itr).second; + } +} + +void KRMaterialManager::add(KRMaterial *new_material) { + // FINDME, TODO - Potential memory leak if multiple materials with the same name are added + std::string lowerName = new_material->getName(); + std::transform(lowerName.begin(), lowerName.end(), + lowerName.begin(), ::tolower); + + m_materials[lowerName] = new_material; +} + +bool KRMaterialManager::load(const char *szName, KRDataBlock *data) { + KRMaterial *pMaterial = NULL; + char szSymbol[16][256]; + data->lock(); + + char *pScan = (char *)data->getStart(); + char *pEnd = (char *)data->getEnd(); + while(pScan < pEnd) { + + // Scan through whitespace + while(pScan < pEnd && (*pScan == ' ' || *pScan == '\t' || *pScan == '\r' || *pScan == '\n')) { + pScan++; + } + + if(*pScan == '#') { + // Line is a comment line + + // Scan to the end of the line + while(pScan < pEnd && *pScan != '\r' && *pScan != '\n') { + pScan++; + } + } else { + int cSymbols = 0; + while(pScan < pEnd && *pScan != '\n' && *pScan != '\r') { + + char *pDest = szSymbol[cSymbols++]; + while(pScan < pEnd && *pScan != ' ' && *pScan != '\n' && *pScan != '\r') { + if(*pScan >= 'A' && *pScan <= 'Z') { + *pDest++ = *pScan++ + 'a' - 'A'; // convert to lower case for case sensitve comparison later + } else { + *pDest++ = *pScan++; + } + } + *pDest = '\0'; + + // Scan through whitespace, but don't advance to next line + while(pScan < pEnd && (*pScan == ' ' || *pScan == '\t')) { + pScan++; + } + } + + if(cSymbols > 0) { + + if(strcmp(szSymbol[0], "newmtl") == 0 && cSymbols >= 2) { + + pMaterial = new KRMaterial(*m_pContext, szSymbol[1]); + m_materials[szSymbol[1]] = pMaterial; + } + if(pMaterial != NULL) { + if(strcmp(szSymbol[0], "alpha_mode") == 0) { + if(cSymbols == 2) { + if(strcmp(szSymbol[1], "test") == 0) { + pMaterial->setAlphaMode(KRMaterial::KRMATERIAL_ALPHA_MODE_TEST); + } else if(strcmp(szSymbol[1], "blendoneside") == 0) { + pMaterial->setAlphaMode(KRMaterial::KRMATERIAL_ALPHA_MODE_BLENDONESIDE); + } else if(strcmp(szSymbol[1], "blendtwoside") == 0) { + pMaterial->setAlphaMode(KRMaterial::KRMATERIAL_ALPHA_MODE_BLENDTWOSIDE); + } else { + pMaterial->setAlphaMode(KRMaterial::KRMATERIAL_ALPHA_MODE_OPAQUE); + } + } + } else if(strcmp(szSymbol[0], "ka") == 0) { + char *pScan2 = szSymbol[1]; + float r = strtof(pScan2, &pScan2); + if(cSymbols == 2) { + pMaterial->setAmbient(Vector3::Create(r, r, r)); + } else if(cSymbols == 4) { + pScan2 = szSymbol[2]; + float g = strtof(pScan2, &pScan2); + pScan2 = szSymbol[3]; + float b = strtof(pScan2, &pScan2); + pMaterial->setAmbient(Vector3::Create(r, g, b)); + } + } else if(strcmp(szSymbol[0], "kd") == 0) { + char *pScan2 = szSymbol[1]; + float r = strtof(pScan2, &pScan2); + if(cSymbols == 2) { + pMaterial->setDiffuse(Vector3::Create(r, r, r)); + } else if(cSymbols == 4) { + pScan2 = szSymbol[2]; + float g = strtof(pScan2, &pScan2); + pScan2 = szSymbol[3]; + float b = strtof(pScan2, &pScan2); + pMaterial->setDiffuse(Vector3::Create(r, g, b)); + } + } else if(strcmp(szSymbol[0], "ks") == 0) { + char *pScan2 = szSymbol[1]; + float r = strtof(pScan2, &pScan2); + if(cSymbols == 2) { + pMaterial->setSpecular(Vector3::Create(r, r, r)); + } else if(cSymbols == 4) { + pScan2 = szSymbol[2]; + float g = strtof(pScan2, &pScan2); + pScan2 = szSymbol[3]; + float b = strtof(pScan2, &pScan2); + pMaterial->setSpecular(Vector3::Create(r, g, b)); + } + } else if(strcmp(szSymbol[0], "kr") == 0) { + char *pScan2 = szSymbol[1]; + float r = strtof(pScan2, &pScan2); + if(cSymbols == 2) { + pMaterial->setReflection(Vector3::Create(r, r, r)); + } else if(cSymbols == 4) { + pScan2 = szSymbol[2]; + float g = strtof(pScan2, &pScan2); + pScan2 = szSymbol[3]; + float b = strtof(pScan2, &pScan2); + pMaterial->setReflection(Vector3::Create(r, g, b)); + } + } else if(strcmp(szSymbol[0], "tr") == 0) { + char *pScan2 = szSymbol[1]; + float a = strtof(pScan2, &pScan2); + pMaterial->setTransparency(a); + } else if(strcmp(szSymbol[0], "ns") == 0) { + char *pScan2 = szSymbol[1]; + float a = strtof(pScan2, &pScan2); + pMaterial->setShininess(a); + } else if(strncmp(szSymbol[0], "map", 3) == 0) { + // Truncate file extension + char *pScan2 = szSymbol[1]; + char *pLastPeriod = NULL; + while(*pScan2 != '\0') { + if(*pScan2 == '.') { + pLastPeriod = pScan2; + } + pScan2++; + } + if(pLastPeriod) { + *pLastPeriod = '\0'; + } + + Vector2 texture_scale = Vector2::Create(1.0f, 1.0f); + Vector2 texture_offset = Vector2::Create(0.0f, 0.0f); + + int iScanSymbol = 2; + int iScaleParam = -1; + int iOffsetParam = -1; + while(iScanSymbol < cSymbols) { + if(strcmp(szSymbol[iScanSymbol], "-s") == 0) { + // Scale + iScaleParam = 0; + iOffsetParam = -1; + } else if(strcmp(szSymbol[iScanSymbol], "-o") == 0) { + // Offset + iOffsetParam = 0; + iScaleParam = -1; + } else { + char *pScan3 = szSymbol[iScanSymbol]; + float v = strtof(pScan3, &pScan3); + if(iScaleParam == 0) { + texture_scale.x = v; + iScaleParam++; + } else if(iScaleParam == 1) { + texture_scale.y = v; + iScaleParam++; + } else if(iOffsetParam == 0) { + texture_offset.x = v; + iOffsetParam++; + } else if(iOffsetParam == 1) { + texture_offset.y = v; + iOffsetParam++; + } + } + iScanSymbol++; + } + + if(strcmp(szSymbol[0], "map_ka") == 0) { + pMaterial->setAmbientMap(szSymbol[1], texture_scale, texture_offset); + } else if(strcmp(szSymbol[0], "map_kd") == 0) { + pMaterial->setDiffuseMap(szSymbol[1], texture_scale, texture_offset); + } else if(strcmp(szSymbol[0], "map_ks") == 0) { + pMaterial->setSpecularMap(szSymbol[1], texture_scale, texture_offset); + } else if(strcmp(szSymbol[0], "map_normal") == 0) { + pMaterial->setNormalMap(szSymbol[1], texture_scale, texture_offset); + } else if(strcmp(szSymbol[0], "map_reflection") == 0) { + pMaterial->setReflectionMap(szSymbol[1], texture_scale, texture_offset); + } else if(strcmp(szSymbol[0], "map_reflectioncube") == 0) { + pMaterial->setReflectionCube(szSymbol[1]); + } + } + } + } + } + + } + data->unlock(); + delete data; + return true; +} diff --git a/kraken/KRMesh.cpp b/kraken/KRMesh.cpp index 6b83209..6477ef8 100755 --- a/kraken/KRMesh.cpp +++ b/kraken/KRMesh.cpp @@ -1,1604 +1,1604 @@ -// -// KRMesh.cpp -// KREngine -// -// Copyright 2012 Kearwood Gilbert. All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, are -// permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of -// conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list -// of conditions and the following disclaimer in the documentation and/or other materials -// provided with the distribution. -// -// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED -// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR -// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// The views and conclusions contained in the software and documentation are those of the -// authors and should not be interpreted as representing official policies, either expressed -// or implied, of Kearwood Gilbert. -// - - -#include "KREngine-common.h" - -#include "KRMesh.h" - -#include "KRShader.h" -#include "KRShaderManager.h" -#include "KRContext.h" -#include "../3rdparty/forsyth/forsyth.h" - -KRMesh::KRMesh(KRContext &context, std::string name) : KRResource(context, name) { - setName(name); - - m_hasTransparency = false; - m_pData = NULL; - m_pMetaData = NULL; - m_pIndexBaseData = NULL; - m_constant = false; -} - -KRMesh::KRMesh(KRContext &context, std::string name, KRDataBlock *data) : KRResource(context, name) { - setName(name); - - m_hasTransparency = false; - m_pData = NULL; - m_pMetaData = NULL; - m_pIndexBaseData = NULL; - m_constant = false; - - loadPack(data); -} - -void KRMesh::setName(const std::string name) { - m_lodCoverage = 100; - m_lodBaseName = name; - - size_t last_underscore_pos = name.find_last_of('_'); - if(last_underscore_pos != std::string::npos) { - // Found an underscore - std::string suffix = name.substr(last_underscore_pos + 1); - if(suffix.find("lod") == 0) { - std::string lod_level_string = suffix.substr(3); - char *end = NULL; - int c = (int)strtol(lod_level_string.c_str(), &end, 10); - if(c >= 0 && c <= 100 && *end == '\0') { - m_lodCoverage = c; - m_lodBaseName = name.substr(0, last_underscore_pos); - } - } - } - -} - -int KRMesh::GetLODCoverage(const std::string &name) -{ - int lod_coverage = 100; - size_t last_underscore_pos = name.find_last_of('_'); - if(last_underscore_pos != std::string::npos) { - // Found an underscore - std::string suffix = name.substr(last_underscore_pos + 1); - if(suffix.find("lod") == 0) { - std::string lod_level_string = suffix.substr(3); - char *end = NULL; - int c = (int)strtol(lod_level_string.c_str(), &end, 10); - if(c >= 0 && c <= 100 && *end == '\0') { - lod_coverage = c; - //m_lodBaseName = name.substr(0, last_underscore_pos); - } - } - } - return lod_coverage; -} - - - -KRMesh::~KRMesh() { - releaseData(); -} - -void KRMesh::releaseData() { - m_hasTransparency = false; - m_submeshes.clear(); - if(m_pIndexBaseData) { - m_pIndexBaseData->unlock(); - delete m_pIndexBaseData; - m_pIndexBaseData = NULL; - } - if(m_pMetaData) { - m_pMetaData->unlock(); - delete m_pMetaData; - m_pMetaData = NULL; - } - if(m_pData) { - delete m_pData; - m_pData = NULL; - } -} - -std::string KRMesh::getExtension() { - return "krmesh"; -} - -bool KRMesh::save(const std::string& path) { - return m_pData->save(path); -} - -bool KRMesh::save(KRDataBlock &data) { - data.append(*m_pData); - return true; -} - -void KRMesh::loadPack(KRDataBlock *data) { - releaseData(); - - m_pData = data; - - pack_header ph; - m_pData->copy((void *)&ph, 0, sizeof(ph)); - m_pMetaData = m_pData->getSubBlock(0, sizeof(pack_header) + sizeof(pack_material) * ph.submesh_count + sizeof(pack_bone) * ph.bone_count); - m_pMetaData->lock(); - - m_pIndexBaseData = m_pData->getSubBlock(sizeof(pack_header) + sizeof(pack_material) * ph.submesh_count + sizeof(pack_bone) * ph.bone_count + KRALIGN(2 * ph.index_count), ph.index_base_count * 8); - m_pIndexBaseData->lock(); - - m_minPoint = Vector3(ph.minx, ph.miny, ph.minz); - m_maxPoint = Vector3(ph.maxx, ph.maxy, ph.maxz); - - updateAttributeOffsets(); -} - -void KRMesh::getMaterials() -{ - if(m_materials.size() == 0) { - - for(std::vector::iterator itr = m_submeshes.begin(); itr != m_submeshes.end(); itr++) { - const char *szMaterialName = (*itr)->szMaterialName; - KRMaterial *pMaterial = nullptr; - if(*szMaterialName != '\0') { - pMaterial = getContext().getMaterialManager()->getMaterial(szMaterialName); - } - m_materials.push_back(pMaterial); - if(pMaterial) { - m_uniqueMaterials.insert(pMaterial); - } else if(*szMaterialName != '\0') { - KRContext::Log(KRContext::LOG_LEVEL_WARNING, "Missing material: %s", szMaterialName); - } - } - - m_hasTransparency = false; - for(std::set::iterator mat_itr = m_uniqueMaterials.begin(); mat_itr != m_uniqueMaterials.end(); mat_itr++) { - if((*mat_itr)->isTransparent()) { - m_hasTransparency = true; - break; - } - } - } -} - -void KRMesh::preStream(float lodCoverage) -{ - getSubmeshes(); - getMaterials(); - - for(std::set::iterator mat_itr = m_uniqueMaterials.begin(); mat_itr != m_uniqueMaterials.end(); mat_itr++) { - (*mat_itr)->preStream(lodCoverage); - } - - int cSubmeshes = m_submeshes.size(); - for(int iSubmesh=0; iSubmeshvbo_data_blocks.begin(); vbo_data_itr != m_submeshes[iSubmesh]->vbo_data_blocks.end(); vbo_data_itr++) { - (*vbo_data_itr)->resetPoolExpiry(lodCoverage); - } - } -} - -void KRMesh::load() -{ - // Load immediately into the GPU rather than passing through the streamer - getSubmeshes(); - getMaterials(); - - int cSubmeshes = m_submeshes.size(); - for(int iSubmesh=0; iSubmeshvbo_data_blocks.begin(); vbo_data_itr != m_submeshes[iSubmesh]->vbo_data_blocks.end(); vbo_data_itr++) { - (*vbo_data_itr)->resetPoolExpiry(1.0f); - (*vbo_data_itr)->load(); - } - } -} - -kraken_stream_level KRMesh::getStreamLevel() -{ - kraken_stream_level stream_level = kraken_stream_level::STREAM_LEVEL_IN_HQ; - getSubmeshes(); - getMaterials(); - - for(std::set::iterator mat_itr = m_uniqueMaterials.begin(); mat_itr != m_uniqueMaterials.end(); mat_itr++) { - stream_level = KRMIN(stream_level, (*mat_itr)->getStreamLevel()); - } - bool all_vbo_data_loaded = true; - bool vbo_data_loaded = false; - int cSubmeshes = m_submeshes.size(); - for(int iSubmesh=0; iSubmeshvbo_data_blocks.begin(); vbo_data_itr != m_submeshes[iSubmesh]->vbo_data_blocks.end(); vbo_data_itr++) { - if((*vbo_data_itr)->isVBOReady()) { - vbo_data_loaded = true; - } else { - all_vbo_data_loaded = false; - } - } - } - - if(!vbo_data_loaded || !all_vbo_data_loaded) { - stream_level = kraken_stream_level::STREAM_LEVEL_OUT; - } - - return stream_level; -} - -void KRMesh::render(const std::string &object_name, KRCamera *pCamera, std::vector &point_lights, std::vector &directional_lights, std::vector&spot_lights, const KRViewport &viewport, const Matrix4 &matModel, KRTexture *pLightMap, KRNode::RenderPass renderPass, const std::vector &bones, const Vector3 &rim_color, float rim_power, float lod_coverage) { - - //fprintf(stderr, "Rendering model: %s\n", m_name.c_str()); - if(renderPass != KRNode::RENDER_PASS_ADDITIVE_PARTICLES && renderPass != KRNode::RENDER_PASS_PARTICLE_OCCLUSION && renderPass != KRNode::RENDER_PASS_VOLUMETRIC_EFFECTS_ADDITIVE) { - preStream(lod_coverage); - if(getStreamLevel() == kraken_stream_level::STREAM_LEVEL_OUT) { - - } else { - - getSubmeshes(); - getMaterials(); - - int cSubmeshes = m_submeshes.size(); - if(renderPass == KRNode::RENDER_PASS_SHADOWMAP) { - for(int iSubmesh=0; iSubmeshisTransparent()) { - // Exclude transparent and semi-transparent meshes from shadow maps - renderSubmesh(iSubmesh, renderPass, object_name, pMaterial->getName(), lod_coverage); - } - } - - } - } else { - // Apply submeshes in per-material batches to reduce number of state changes - for(std::set::iterator mat_itr = m_uniqueMaterials.begin(); mat_itr != m_uniqueMaterials.end(); mat_itr++) { - for(int iSubmesh=0; iSubmeshisTransparent() && renderPass != KRNode::RENDER_PASS_FORWARD_TRANSPARENT) || (pMaterial->isTransparent() && renderPass == KRNode::RENDER_PASS_FORWARD_TRANSPARENT)) { - std::vector bone_bind_poses; - for(int i=0; i < bones.size(); i++) { - bone_bind_poses.push_back(getBoneBindPose(i)); - } - if(pMaterial->bind(pCamera, point_lights, directional_lights, spot_lights, bones, bone_bind_poses, viewport, matModel, pLightMap, renderPass, rim_color, rim_power, lod_coverage)) { - - switch(pMaterial->getAlphaMode()) { - case KRMaterial::KRMATERIAL_ALPHA_MODE_OPAQUE: // Non-transparent materials - case KRMaterial::KRMATERIAL_ALPHA_MODE_TEST: // Alpha in diffuse texture is interpreted as punch-through when < 0.5 - renderSubmesh(iSubmesh, renderPass, object_name, pMaterial->getName(), lod_coverage); - break; - case KRMaterial::KRMATERIAL_ALPHA_MODE_BLENDONESIDE: // Blended alpha with backface culling - renderSubmesh(iSubmesh, renderPass, object_name, pMaterial->getName(), lod_coverage); - break; - case KRMaterial::KRMATERIAL_ALPHA_MODE_BLENDTWOSIDE: // Blended alpha rendered in two passes. First pass renders backfaces; second pass renders frontfaces. - // Render back faces first - GLDEBUG(glCullFace(GL_FRONT)); - renderSubmesh(iSubmesh, renderPass, object_name, pMaterial->getName(), lod_coverage); - - // Render front faces second - GLDEBUG(glCullFace(GL_BACK)); - renderSubmesh(iSubmesh, renderPass, object_name, pMaterial->getName(), lod_coverage); - break; - } - } - } - } - } - } - } - } - } -} - -GLfloat KRMesh::getMaxDimension() { - GLfloat m = 0.0; - if(m_maxPoint.x - m_minPoint.x > m) m = m_maxPoint.x - m_minPoint.x; - if(m_maxPoint.y - m_minPoint.y > m) m = m_maxPoint.y - m_minPoint.y; - if(m_maxPoint.z - m_minPoint.z > m) m = m_maxPoint.z - m_minPoint.z; - return m; -} - -bool KRMesh::hasTransparency() { - return m_hasTransparency; -} - - -void KRMesh::getSubmeshes() { - if(m_submeshes.size() == 0) { - pack_header *pHeader = getHeader(); - pack_material *pPackMaterials = (pack_material *)(pHeader+1); - m_submeshes.clear(); - for(int iMaterial=0; iMaterial < pHeader->submesh_count; iMaterial++) { - pack_material *pPackMaterial = pPackMaterials + iMaterial; - - Submesh *pSubmesh = new Submesh(); - pSubmesh->start_vertex = pPackMaterial->start_vertex; - pSubmesh->vertex_count = pPackMaterial->vertex_count; - - strncpy(pSubmesh->szMaterialName, pPackMaterial->szName, KRENGINE_MAX_NAME_LENGTH); - pSubmesh->szMaterialName[KRENGINE_MAX_NAME_LENGTH-1] = '\0'; - //fprintf(stderr, "Submesh material: \"%s\"\n", pSubmesh->szMaterialName); - m_submeshes.push_back(pSubmesh); - } - createDataBlocks(m_constant ? KRMeshManager::KRVBOData::CONSTANT : KRMeshManager::KRVBOData::STREAMING); - } -} - -void KRMesh::createDataBlocks(KRMeshManager::KRVBOData::vbo_type t) -{ - int cSubmeshes = m_submeshes.size(); - for(int iSubmesh=0; iSubmesh < cSubmeshes; iSubmesh++) { - - Submesh *pSubmesh = m_submeshes[iSubmesh]; - int cVertexes = pSubmesh->vertex_count; - int vertex_data_offset = getVertexDataOffset(); - int index_data_offset = getIndexDataOffset(); - pack_header *pHeader = getHeader(); - int32_t vertex_attrib_flags = pHeader->vertex_attrib_flags; - int32_t vertex_count = pHeader->vertex_count; - - int vbo_index=0; - if(getModelFormat() == KRENGINE_MODEL_FORMAT_INDEXED_TRIANGLES) { - - int index_group = getSubmesh(iSubmesh)->index_group; - int index_group_offset = getSubmesh(iSubmesh)->index_group_offset; - while(cVertexes > 0) { - - int start_index_offset, start_vertex_offset, index_count, vertex_count; - getIndexedRange(index_group++, start_index_offset, start_vertex_offset, index_count, vertex_count); - - if(m_submeshes[iSubmesh]->vertex_data_blocks.size() <= vbo_index) { - KRDataBlock *vertex_data_block = m_pData->getSubBlock(vertex_data_offset + start_vertex_offset * m_vertex_size, vertex_count * m_vertex_size); - KRDataBlock *index_data_block = m_pData->getSubBlock(index_data_offset + start_index_offset * 2, index_count * 2); - KRMeshManager::KRVBOData *vbo_data_block = new KRMeshManager::KRVBOData(getContext().getMeshManager(), *vertex_data_block, *index_data_block, vertex_attrib_flags, true, t); - m_submeshes[iSubmesh]->vertex_data_blocks.push_back(vertex_data_block); - m_submeshes[iSubmesh]->index_data_blocks.push_back(index_data_block); - m_submeshes[iSubmesh]->vbo_data_blocks.push_back(vbo_data_block); - } - vbo_index++; - - int vertex_draw_count = cVertexes; - if(vertex_draw_count > index_count - index_group_offset) vertex_draw_count = index_count - index_group_offset; - - cVertexes -= vertex_draw_count; - index_group_offset = 0; - } - - } else { - int cBuffers = (vertex_count + MAX_VBO_SIZE - 1) / MAX_VBO_SIZE; - int iVertex = pSubmesh->start_vertex; - int iBuffer = iVertex / MAX_VBO_SIZE; - iVertex = iVertex % MAX_VBO_SIZE; - while(cVertexes > 0) { - GLsizei cBufferVertexes = iBuffer < cBuffers - 1 ? MAX_VBO_SIZE : vertex_count % MAX_VBO_SIZE; - int vertex_size = m_vertex_size; - - if(m_submeshes[iSubmesh]->vertex_data_blocks.size() <= vbo_index) { - KRDataBlock *index_data_block = NULL; - KRDataBlock *vertex_data_block = m_pData->getSubBlock(vertex_data_offset + iBuffer * MAX_VBO_SIZE * vertex_size, vertex_size * cBufferVertexes); - KRMeshManager::KRVBOData *vbo_data_block = new KRMeshManager::KRVBOData(getContext().getMeshManager(), *vertex_data_block, *index_data_block, vertex_attrib_flags, true, t); - m_submeshes[iSubmesh]->vertex_data_blocks.push_back(vertex_data_block); - m_submeshes[iSubmesh]->vbo_data_blocks.push_back(vbo_data_block); - } - vbo_index++; - - if(iVertex + cVertexes >= MAX_VBO_SIZE) { - assert(iVertex + (MAX_VBO_SIZE - iVertex) <= cBufferVertexes); - - cVertexes -= (MAX_VBO_SIZE - iVertex); - iVertex = 0; - iBuffer++; - } else { - assert(iVertex + cVertexes <= cBufferVertexes); - - cVertexes = 0; - } - - } - } - } -} - -void KRMesh::renderSubmesh(int iSubmesh, KRNode::RenderPass renderPass, const std::string &object_name, const std::string &material_name, float lodCoverage) { - getSubmeshes(); - - Submesh *pSubmesh = m_submeshes[iSubmesh]; - int cVertexes = pSubmesh->vertex_count; - - int vbo_index=0; - if(getModelFormat() == KRENGINE_MODEL_FORMAT_INDEXED_TRIANGLES) { - - int index_group = getSubmesh(iSubmesh)->index_group; - int index_group_offset = getSubmesh(iSubmesh)->index_group_offset; - while(cVertexes > 0) { - - int start_index_offset, start_vertex_offset, index_count, vertex_count; - getIndexedRange(index_group++, start_index_offset, start_vertex_offset, index_count, vertex_count); - - KRMeshManager::KRVBOData *vbo_data_block = m_submeshes[iSubmesh]->vbo_data_blocks[vbo_index++]; - assert(vbo_data_block->isVBOReady()); - - m_pContext->getMeshManager()->bindVBO(vbo_data_block, lodCoverage); - - - int vertex_draw_count = cVertexes; - if(vertex_draw_count > index_count - index_group_offset) vertex_draw_count = index_count - index_group_offset; - - glDrawElements(GL_TRIANGLES, vertex_draw_count, GL_UNSIGNED_SHORT, BUFFER_OFFSET(index_group_offset * 2)); - m_pContext->getMeshManager()->log_draw_call(renderPass, object_name, material_name, vertex_draw_count); - cVertexes -= vertex_draw_count; - index_group_offset = 0; - } - - } else { - int cBuffers = (cVertexes + MAX_VBO_SIZE - 1) / MAX_VBO_SIZE; - int iVertex = pSubmesh->start_vertex; - int iBuffer = iVertex / MAX_VBO_SIZE; - iVertex = iVertex % MAX_VBO_SIZE; - while(cVertexes > 0) { - GLsizei cBufferVertexes = iBuffer < cBuffers - 1 ? MAX_VBO_SIZE : cVertexes % MAX_VBO_SIZE; - - KRMeshManager::KRVBOData *vbo_data_block = m_submeshes[iSubmesh]->vbo_data_blocks[vbo_index++]; - assert(vbo_data_block->isVBOReady()); - m_pContext->getMeshManager()->bindVBO(vbo_data_block, lodCoverage); - - - if(iVertex + cVertexes >= MAX_VBO_SIZE) { - assert(iVertex + (MAX_VBO_SIZE - iVertex) <= cBufferVertexes); - switch (getModelFormat()) { - case KRENGINE_MODEL_FORMAT_TRIANGLES: - GLDEBUG(glDrawArrays(GL_TRIANGLES, iVertex, (MAX_VBO_SIZE - iVertex))); - break; - case KRENGINE_MODEL_FORMAT_STRIP: - GLDEBUG(glDrawArrays(GL_TRIANGLE_STRIP, iVertex, (MAX_VBO_SIZE - iVertex))); - break; - case KRENGINE_MODEL_FORMAT_INDEXED_TRIANGLES: - GLDEBUG(glDrawArrays(GL_TRIANGLES, iVertex, (MAX_VBO_SIZE - iVertex))); - break; - case KRENGINE_MODEL_FORMAT_INDEXED_STRIP: - GLDEBUG(glDrawArrays(GL_TRIANGLE_STRIP, iVertex, (MAX_VBO_SIZE - iVertex))); - break; - default: - break; - } - m_pContext->getMeshManager()->log_draw_call(renderPass, object_name, material_name, (MAX_VBO_SIZE - iVertex)); - - cVertexes -= (MAX_VBO_SIZE - iVertex); - iVertex = 0; - iBuffer++; - } else { - assert(iVertex + cVertexes <= cBufferVertexes); - - switch (getModelFormat()) { - case KRENGINE_MODEL_FORMAT_TRIANGLES: - GLDEBUG(glDrawArrays(GL_TRIANGLES, iVertex, cVertexes)); - break; - case KRENGINE_MODEL_FORMAT_STRIP: - GLDEBUG(glDrawArrays(GL_TRIANGLE_STRIP, iVertex, cVertexes)); - break; - default: - break; - } - m_pContext->getMeshManager()->log_draw_call(renderPass, object_name, material_name, cVertexes); - - cVertexes = 0; - } - - } - } -} - -void KRMesh::LoadData(const KRMesh::mesh_info &mi, bool calculate_normals, bool calculate_tangents) { - - releaseData(); - - // TODO, FINDME - These values should be passed as a parameter and set by GUI flags - bool use_short_vertexes = false; - bool use_short_normals = true; - bool use_short_tangents = true; - bool use_short_uva = true; - bool use_short_uvb = true; - - if(use_short_vertexes) { - for(std::vector::const_iterator itr=mi.vertices.begin(); itr != mi.vertices.end(); itr++) { - if(fabsf((*itr).x) > 1.0f || fabsf((*itr).y) > 1.0f || fabsf((*itr).z) > 1.0f) { - use_short_vertexes = false; - } - } - } - - if(use_short_uva) { - for(std::vector::const_iterator itr=mi.uva.begin(); itr != mi.uva.end(); itr++) { - if(fabsf((*itr).x) > 1.0f || fabsf((*itr).y) > 1.0f) { - use_short_uva = false; - } - } - } - - if(use_short_uvb) { - for(std::vector::const_iterator itr=mi.uvb.begin(); itr != mi.uvb.end(); itr++) { - if(fabsf((*itr).x) > 1.0f || fabsf((*itr).y) > 1.0f) { - use_short_uvb = false; - } - } - } - - __int32_t vertex_attrib_flags = 0; - if(mi.vertices.size()) { - if(use_short_vertexes) { - vertex_attrib_flags |= (1 << KRENGINE_ATTRIB_VERTEX_SHORT); - } else { - vertex_attrib_flags |= (1 << KRENGINE_ATTRIB_VERTEX); - } - } - if(mi.normals.size() || calculate_normals) { - if(use_short_normals) { - vertex_attrib_flags += (1 << KRENGINE_ATTRIB_NORMAL_SHORT); - } else { - vertex_attrib_flags += (1 << KRENGINE_ATTRIB_NORMAL); - } - } - if(mi.tangents.size() || calculate_tangents) { - if(use_short_tangents) { - vertex_attrib_flags += (1 << KRENGINE_ATTRIB_TANGENT_SHORT); - } else { - vertex_attrib_flags += (1 << KRENGINE_ATTRIB_TANGENT); - } - } - if(mi.uva.size()) { - if(use_short_uva) { - vertex_attrib_flags += (1 << KRENGINE_ATTRIB_TEXUVA_SHORT); - } else { - vertex_attrib_flags += (1 << KRENGINE_ATTRIB_TEXUVA); - } - } - if(mi.uvb.size()) { - if(use_short_uvb) { - vertex_attrib_flags += (1 << KRENGINE_ATTRIB_TEXUVB_SHORT); - } else { - vertex_attrib_flags += (1 << KRENGINE_ATTRIB_TEXUVB); - } - } - if(mi.bone_names.size()) { - vertex_attrib_flags += (1 << KRENGINE_ATTRIB_BONEINDEXES) + (1 << KRENGINE_ATTRIB_BONEWEIGHTS); - } - size_t vertex_size = VertexSizeForAttributes(vertex_attrib_flags); - size_t index_count = mi.vertex_indexes.size(); - size_t index_base_count = mi.vertex_index_bases.size(); - size_t submesh_count = mi.submesh_lengths.size(); - size_t vertex_count = mi.vertices.size(); - size_t bone_count = mi.bone_names.size(); - size_t new_file_size = sizeof(pack_header) + sizeof(pack_material) * submesh_count + sizeof(pack_bone) * bone_count + KRALIGN(2 * index_count) + KRALIGN(8 * index_base_count) + vertex_size * vertex_count; - m_pData = new KRDataBlock(); - m_pMetaData = m_pData; - m_pData->expand(new_file_size); - m_pData->lock(); - pack_header *pHeader = getHeader(); - memset(pHeader, 0, sizeof(pack_header)); - pHeader->vertex_attrib_flags = vertex_attrib_flags; - pHeader->submesh_count = (__int32_t)submesh_count; - pHeader->vertex_count = (__int32_t)vertex_count; - pHeader->bone_count = (__int32_t)bone_count; - pHeader->index_count = (__int32_t)index_count; - pHeader->index_base_count = (__int32_t)index_base_count; - pHeader->model_format = mi.format; - strcpy(pHeader->szTag, "KROBJPACK1.2 "); - updateAttributeOffsets(); - - pack_material *pPackMaterials = (pack_material *)(pHeader+1); - - for(int iMaterial=0; iMaterial < pHeader->submesh_count; iMaterial++) { - pack_material *pPackMaterial = pPackMaterials + iMaterial; - pPackMaterial->start_vertex = mi.submesh_starts[iMaterial]; - pPackMaterial->vertex_count = mi.submesh_lengths[iMaterial]; - memset(pPackMaterial->szName, 0, KRENGINE_MAX_NAME_LENGTH); - strncpy(pPackMaterial->szName, mi.material_names[iMaterial].c_str(), KRENGINE_MAX_NAME_LENGTH); - } - - for(int bone_index=0; bone_index < bone_count; bone_index++) { - pack_bone *bone = getBone(bone_index); - memset(bone->szName, 0, KRENGINE_MAX_NAME_LENGTH); - strncpy(bone->szName, mi.bone_names[bone_index].c_str(), KRENGINE_MAX_NAME_LENGTH); - memcpy(bone->bind_pose, mi.bone_bind_poses[bone_index].c, sizeof(float) * 16); - } - - bool bFirstVertex = true; - - memset(getVertexData(), 0, m_vertex_size * mi.vertices.size()); - for(int iVertex=0; iVertex < mi.vertices.size(); iVertex++) { - Vector3 source_vertex = mi.vertices[iVertex]; - setVertexPosition(iVertex, source_vertex); - if(mi.bone_names.size()) { - for(int bone_weight_index=0; bone_weight_index m_maxPoint.x) m_maxPoint.x = source_vertex.x; - if(source_vertex.y > m_maxPoint.y) m_maxPoint.y = source_vertex.y; - if(source_vertex.z > m_maxPoint.z) m_maxPoint.z = source_vertex.z; - } - if(mi.uva.size() > iVertex) { - setVertexUVA(iVertex, mi.uva[iVertex]); - } - if(mi.uvb.size() > iVertex) { - setVertexUVB(iVertex, mi.uvb[iVertex]); - } - if(mi.normals.size() > iVertex) { - setVertexNormal(iVertex, Vector3::Normalize(mi.normals[iVertex])); - } - if(mi.tangents.size() > iVertex) { - setVertexTangent(iVertex, Vector3::Normalize(mi.tangents[iVertex])); - } - } - - pHeader->minx = m_minPoint.x; - pHeader->miny = m_minPoint.y; - pHeader->minz = m_minPoint.z; - pHeader->maxx = m_maxPoint.x; - pHeader->maxy = m_maxPoint.y; - pHeader->maxz = m_maxPoint.z; - - - __uint16_t *index_data = getIndexData(); - for(std::vector<__uint16_t>::const_iterator itr=mi.vertex_indexes.begin(); itr != mi.vertex_indexes.end(); itr++) { - *index_data++ = *itr; - } - - __uint32_t *index_base_data = getIndexBaseData(); - for(std::vector >::const_iterator itr=mi.vertex_index_bases.begin(); itr != mi.vertex_index_bases.end(); itr++) { - *index_base_data++ = (*itr).first; - *index_base_data++ = (*itr).second; - } - - if(getModelFormat() == KRENGINE_MODEL_FORMAT_TRIANGLES) { - // Calculate missing surface normals and tangents - //cout << " Calculate surface normals and tangents\n"; - if(calculate_normals || calculate_tangents) { - // NOTE: This will not work properly if the vertices are already indexed - for(int iVertex=0; iVertex < mi.vertices.size(); iVertex+= 3) { - Vector3 p1 = getVertexPosition(iVertex); - Vector3 p2 = getVertexPosition(iVertex+1); - Vector3 p3 = getVertexPosition(iVertex+2); - Vector3 v1 = p2 - p1; - Vector3 v2 = p3 - p1; - - - // -- Calculate normal if missing -- - if(calculate_normals) { - Vector3 first_normal = getVertexNormal(iVertex); - if(first_normal.x == 0.0f && first_normal.y == 0.0f && first_normal.z == 0.0f) { - // Note - We don't take into consideration smoothing groups or smoothing angles when generating normals; all generated normals represent flat shaded polygons - Vector3 normal = Vector3::Cross(v1, v2); - - normal.normalize(); - setVertexNormal(iVertex, normal); - setVertexNormal(iVertex+1, normal); - setVertexNormal(iVertex+2, normal); - } - } - - // -- Calculate tangent vector for normal mapping -- - if(calculate_tangents) { - Vector3 first_tangent = getVertexTangent(iVertex); - if(first_tangent.x == 0.0f && first_tangent.y == 0.0f && first_tangent.z == 0.0f) { - - Vector2 uv0 = getVertexUVA(iVertex); - Vector2 uv1 = getVertexUVA(iVertex + 1); - Vector2 uv2 = getVertexUVA(iVertex + 2); - - Vector2 st1 = Vector2(uv1.x - uv0.x, uv1.y - uv0.y); - Vector2 st2 = Vector2(uv2.x - uv0.x, uv2.y - uv0.y); - double coef = 1/ (st1.x * st2.y - st2.x * st1.y); - - Vector3 tangent( - coef * ((v1.x * st2.y) + (v2.x * -st1.y)), - coef * ((v1.y * st2.y) + (v2.y * -st1.y)), - coef * ((v1.z * st2.y) + (v2.z * -st1.y)) - ); - - tangent.normalize(); - setVertexTangent(iVertex, tangent); - setVertexTangent(iVertex+1, tangent); - setVertexTangent(iVertex+2, tangent); - } - } - } - } - } - m_pData->unlock(); - - // ---- - - pack_header ph; - m_pData->copy((void *)&ph, 0, sizeof(ph)); - m_pMetaData = m_pData->getSubBlock(0, sizeof(pack_header) + sizeof(pack_material) * ph.submesh_count + sizeof(pack_bone) * ph.bone_count); - m_pMetaData->lock(); - m_pIndexBaseData = m_pData->getSubBlock(sizeof(pack_header) + sizeof(pack_material) * ph.submesh_count + sizeof(pack_bone) * ph.bone_count + KRALIGN(2 * ph.index_count), ph.index_base_count * 8); - m_pIndexBaseData->lock(); - - // ---- - - optimize(); -} - -Vector3 KRMesh::getMinPoint() const { - return m_minPoint; -} - -Vector3 KRMesh::getMaxPoint() const { - return m_maxPoint; -} - -int KRMesh::getLODCoverage() const { - return m_lodCoverage; -} - -std::string KRMesh::getLODBaseName() const { - return m_lodBaseName; -} - -// Predicate used with std::sort to sort by highest detail model first, decending to lowest detail LOD model -bool KRMesh::lod_sort_predicate(const KRMesh *m1, const KRMesh *m2) -{ - return m1->m_lodCoverage > m2->m_lodCoverage; -} - -bool KRMesh::has_vertex_attribute(vertex_attrib_t attribute_type) const -{ - //return (getHeader()->vertex_attrib_flags & (1 << attribute_type)) != 0; - return has_vertex_attribute(getHeader()->vertex_attrib_flags, attribute_type); -} - -bool KRMesh::has_vertex_attribute(int vertex_attrib_flags, vertex_attrib_t attribute_type) -{ - return (vertex_attrib_flags & (1 << attribute_type)) != 0; -} - -KRMesh::pack_header *KRMesh::getHeader() const -{ - return (pack_header *)m_pMetaData->getStart(); -} - -KRMesh::pack_bone *KRMesh::getBone(int index) -{ - pack_header *header = getHeader(); - return (pack_bone *)((unsigned char *)m_pMetaData->getStart() + sizeof(pack_header) + sizeof(pack_material) * header->submesh_count + sizeof(pack_bone) * index); -} - -unsigned char *KRMesh::getVertexData() const { - return ((unsigned char *)m_pData->getStart()) + getVertexDataOffset(); -} - -size_t KRMesh::getVertexDataOffset() const { - pack_header *pHeader = getHeader(); - return sizeof(pack_header) + sizeof(pack_material) * pHeader->submesh_count + sizeof(pack_bone) * pHeader->bone_count + KRALIGN(2 * pHeader->index_count) + KRALIGN(8 * pHeader->index_base_count); -} - -__uint16_t *KRMesh::getIndexData() const { - - return (__uint16_t *)((unsigned char *)m_pData->getStart() + getIndexDataOffset()); -} - -size_t KRMesh::getIndexDataOffset() const { - pack_header *pHeader = getHeader(); - return sizeof(pack_header) + sizeof(pack_material) * pHeader->submesh_count + sizeof(pack_bone) * pHeader->bone_count; -} - -__uint32_t *KRMesh::getIndexBaseData() const { - if(m_pIndexBaseData == NULL) { - pack_header *pHeader = getHeader(); - return (__uint32_t *)((unsigned char *)m_pData->getStart() + sizeof(pack_header) + sizeof(pack_material) * pHeader->submesh_count + sizeof(pack_bone) * pHeader->bone_count + KRALIGN(2 * pHeader->index_count)); - } else { - return (__uint32_t *)m_pIndexBaseData->getStart(); - } -} - - -KRMesh::pack_material *KRMesh::getSubmesh(int mesh_index) const -{ - return (pack_material *)((unsigned char *)m_pMetaData->getStart() + sizeof(pack_header)) + mesh_index; -} - -unsigned char *KRMesh::getVertexData(int index) const -{ - return getVertexData() + m_vertex_size * index; -} - -int KRMesh::getSubmeshCount() const -{ - pack_header *header = getHeader(); - int submesh_count = header->submesh_count; - return submesh_count; -} - -int KRMesh::getVertexCount(int submesh) const -{ - return getSubmesh(submesh)->vertex_count; -} - -Vector3 KRMesh::getVertexPosition(int index) const -{ - if(has_vertex_attribute(KRENGINE_ATTRIB_VERTEX_SHORT)) { - short *v = (short *)(getVertexData(index) + m_vertex_attribute_offset[KRENGINE_ATTRIB_VERTEX_SHORT]); - return Vector3((float)v[0] / 32767.0f, (float)v[1] / 32767.0f, (float)v[2] / 32767.0f); - } else if(has_vertex_attribute(KRENGINE_ATTRIB_VERTEX)) { - return Vector3((float *)(getVertexData(index) + m_vertex_attribute_offset[KRENGINE_ATTRIB_VERTEX])); - } else { - return Vector3::Zero(); - } -} - -Vector3 KRMesh::getVertexNormal(int index) const -{ - if(has_vertex_attribute(KRENGINE_ATTRIB_NORMAL_SHORT)) { - short *v = (short *)(getVertexData(index) + m_vertex_attribute_offset[KRENGINE_ATTRIB_NORMAL_SHORT]); - return Vector3((float)v[0] / 32767.0f, (float)v[1] / 32767.0f, (float)v[2] / 32767.0f); - } else if(has_vertex_attribute(KRENGINE_ATTRIB_NORMAL)) { - return Vector3((float *)(getVertexData(index) + m_vertex_attribute_offset[KRENGINE_ATTRIB_NORMAL])); - } else { - return Vector3::Zero(); - } -} - -Vector3 KRMesh::getVertexTangent(int index) const -{ - if(has_vertex_attribute(KRENGINE_ATTRIB_TANGENT_SHORT)) { - short *v = (short *)(getVertexData(index) + m_vertex_attribute_offset[KRENGINE_ATTRIB_TANGENT_SHORT]); - return Vector3((float)v[0] / 32767.0f, (float)v[1] / 32767.0f, (float)v[2] / 32767.0f); - } else if(has_vertex_attribute(KRENGINE_ATTRIB_TANGENT)) { - return Vector3((float *)(getVertexData(index) + m_vertex_attribute_offset[KRENGINE_ATTRIB_TANGENT])); - } else { - return Vector3::Zero(); - } -} - -Vector2 KRMesh::getVertexUVA(int index) const -{ - if(has_vertex_attribute(KRENGINE_ATTRIB_TEXUVA_SHORT)) { - short *v = (short *)(getVertexData(index) + m_vertex_attribute_offset[KRENGINE_ATTRIB_TEXUVA_SHORT]); - return Vector2((float)v[0] / 32767.0f, (float)v[1] / 32767.0f); - } else if(has_vertex_attribute(KRENGINE_ATTRIB_TEXUVA)) { - return Vector2((float *)(getVertexData(index) + m_vertex_attribute_offset[KRENGINE_ATTRIB_TEXUVA])); - } else { - return Vector2::Zero(); - } -} - -Vector2 KRMesh::getVertexUVB(int index) const -{ - if(has_vertex_attribute(KRENGINE_ATTRIB_TEXUVB_SHORT)) { - short *v = (short *)(getVertexData(index) + m_vertex_attribute_offset[KRENGINE_ATTRIB_TEXUVB_SHORT]); - return Vector2((float)v[0] / 32767.0f, (float)v[1] / 32767.0f); - } else if(has_vertex_attribute(KRENGINE_ATTRIB_TEXUVB)) { - return Vector2((float *)(getVertexData(index) + m_vertex_attribute_offset[KRENGINE_ATTRIB_TEXUVB])); - } else { - return Vector2::Zero(); - } -} - -void KRMesh::setVertexPosition(int index, const Vector3 &v) -{ - if(has_vertex_attribute(KRENGINE_ATTRIB_VERTEX_SHORT)) { - short *vert = (short *)(getVertexData(index) + m_vertex_attribute_offset[KRENGINE_ATTRIB_VERTEX_SHORT]); - vert[0] = v.x * 32767.0f; - vert[1] = v.y * 32767.0f; - vert[2] = v.z * 32767.0f; - } else if(has_vertex_attribute(KRENGINE_ATTRIB_VERTEX)) { - float *vert = (float *)(getVertexData(index) + m_vertex_attribute_offset[KRENGINE_ATTRIB_VERTEX]); - vert[0] = v.x; - vert[1] = v.y; - vert[2] = v.z; - } -} - -void KRMesh::setVertexNormal(int index, const Vector3 &v) -{ - if(has_vertex_attribute(KRENGINE_ATTRIB_NORMAL_SHORT)) { - short *vert = (short *)(getVertexData(index) + m_vertex_attribute_offset[KRENGINE_ATTRIB_NORMAL_SHORT]); - vert[0] = v.x * 32767.0f; - vert[1] = v.y * 32767.0f; - vert[2] = v.z * 32767.0f; - } else if(has_vertex_attribute(KRENGINE_ATTRIB_NORMAL)) { - float *vert = (float *)(getVertexData(index) + m_vertex_attribute_offset[KRENGINE_ATTRIB_NORMAL]); - vert[0] = v.x; - vert[1] = v.y; - vert[2] = v.z; - } -} - -void KRMesh::setVertexTangent(int index, const Vector3 & v) -{ - if(has_vertex_attribute(KRENGINE_ATTRIB_TANGENT_SHORT)) { - short *vert = (short *)(getVertexData(index) + m_vertex_attribute_offset[KRENGINE_ATTRIB_TANGENT_SHORT]); - vert[0] = v.x * 32767.0f; - vert[1] = v.y * 32767.0f; - vert[2] = v.z * 32767.0f; - } else if(has_vertex_attribute(KRENGINE_ATTRIB_TANGENT)) { - float *vert = (float *)(getVertexData(index) + m_vertex_attribute_offset[KRENGINE_ATTRIB_TANGENT]); - vert[0] = v.x; - vert[1] = v.y; - vert[2] = v.z; - } -} - -void KRMesh::setVertexUVA(int index, const Vector2 &v) -{ - if(has_vertex_attribute(KRENGINE_ATTRIB_TEXUVA_SHORT)) { - short *vert = (short *)(getVertexData(index) + m_vertex_attribute_offset[KRENGINE_ATTRIB_TEXUVA_SHORT]); - vert[0] = v.x * 32767.0f; - vert[1] = v.y * 32767.0f; - } else if(has_vertex_attribute(KRENGINE_ATTRIB_TEXUVA)) { - float *vert = (float *)(getVertexData(index) + m_vertex_attribute_offset[KRENGINE_ATTRIB_TEXUVA]); - vert[0] = v.x; - vert[1] = v.y; - } -} - -void KRMesh::setVertexUVB(int index, const Vector2 &v) -{ - if(has_vertex_attribute(KRENGINE_ATTRIB_TEXUVB_SHORT)) { - short *vert = (short *)(getVertexData(index) + m_vertex_attribute_offset[KRENGINE_ATTRIB_TEXUVB_SHORT]); - vert[0] = v.x * 32767.0f; - vert[1] = v.y * 32767.0f; - } else if(has_vertex_attribute(KRENGINE_ATTRIB_TEXUVB)) { - float *vert = (float *)(getVertexData(index) + m_vertex_attribute_offset[KRENGINE_ATTRIB_TEXUVB]); - vert[0] = v.x; - vert[1] = v.y; - } -} - - -int KRMesh::getBoneIndex(int index, int weight_index) const -{ - unsigned char *vert = (unsigned char *)(getVertexData(index) + m_vertex_attribute_offset[KRENGINE_ATTRIB_BONEINDEXES]); - return vert[weight_index]; -} - -void KRMesh::setBoneIndex(int index, int weight_index, int bone_index) -{ - unsigned char *vert = (unsigned char *)(getVertexData(index) + m_vertex_attribute_offset[KRENGINE_ATTRIB_BONEINDEXES]); - vert[weight_index] = bone_index; -} - -float KRMesh::getBoneWeight(int index, int weight_index) const -{ - float *vert = (float *)(getVertexData(index) + m_vertex_attribute_offset[KRENGINE_ATTRIB_BONEWEIGHTS]); - return vert[weight_index]; -} - -void KRMesh::setBoneWeight(int index, int weight_index, float bone_weight) -{ - float *vert = (float *)(getVertexData(index) + m_vertex_attribute_offset[KRENGINE_ATTRIB_BONEWEIGHTS]); - vert[weight_index] = bone_weight; -} - -size_t KRMesh::VertexSizeForAttributes(__int32_t vertex_attrib_flags) -{ - size_t data_size = 0; - if(has_vertex_attribute(vertex_attrib_flags, KRENGINE_ATTRIB_VERTEX)) { - data_size += sizeof(float) * 3; - } - if(has_vertex_attribute(vertex_attrib_flags, KRENGINE_ATTRIB_NORMAL)) { - data_size += sizeof(float) * 3; - } - if(has_vertex_attribute(vertex_attrib_flags, KRENGINE_ATTRIB_TANGENT)) { - data_size += sizeof(float) * 3; - } - if(has_vertex_attribute(vertex_attrib_flags, KRENGINE_ATTRIB_TEXUVA)) { - data_size += sizeof(float) * 2; - } - if(has_vertex_attribute(vertex_attrib_flags, KRENGINE_ATTRIB_TEXUVB)) { - data_size += sizeof(float) * 2; - } - if(has_vertex_attribute(vertex_attrib_flags, KRENGINE_ATTRIB_BONEINDEXES)) { - data_size += 4; // 4 bytes - } - if(has_vertex_attribute(vertex_attrib_flags, KRENGINE_ATTRIB_BONEWEIGHTS)) { - data_size += sizeof(float) * 4; - } - if(has_vertex_attribute(vertex_attrib_flags, KRENGINE_ATTRIB_VERTEX_SHORT)) { - data_size += sizeof(short) * 4; // Extra short added in order to maintain 32-bit alignment. TODO, FINDME - Perhaps we can bind this as a vec4 and use the 4th component for another attribute... - } - if(has_vertex_attribute(vertex_attrib_flags, KRENGINE_ATTRIB_NORMAL_SHORT)) { - data_size += sizeof(short) * 4; // Extra short added in order to maintain 32-bit alignment. TODO, FINDME - Perhaps we can bind this as a vec4 and use the 4th component for another attribute... - } - if(has_vertex_attribute(vertex_attrib_flags, KRENGINE_ATTRIB_TANGENT_SHORT)) { - data_size += sizeof(short) * 4; // Extra short added in order to maintain 32-bit alignment. TODO, FINDME - Perhaps we can bind this as a vec4 and use the 4th component for another attribute... - } - if(has_vertex_attribute(vertex_attrib_flags, KRENGINE_ATTRIB_TEXUVA_SHORT)) { - data_size += sizeof(short) * 2; - } - if(has_vertex_attribute(vertex_attrib_flags, KRENGINE_ATTRIB_TEXUVB_SHORT)) { - data_size += sizeof(short) * 2; - } - return data_size; -} - -void KRMesh::updateAttributeOffsets() -{ - pack_header *header = getHeader(); - int mask = 0; - for(int i=0; i < KRENGINE_NUM_ATTRIBUTES; i++) { - if(has_vertex_attribute((vertex_attrib_t)i)) { - m_vertex_attribute_offset[i] = VertexSizeForAttributes(header->vertex_attrib_flags & mask); - } else { - m_vertex_attribute_offset[i] = -1; - } - mask = (mask << 1) | 1; - } - m_vertex_size = VertexSizeForAttributes(header->vertex_attrib_flags); -} - -size_t KRMesh::AttributeOffset(__int32_t vertex_attrib, __int32_t vertex_attrib_flags) -{ - int mask = 0; - for(int i=0; i < vertex_attrib; i++) { - if(vertex_attrib_flags & (1 << i)) { - mask |= (1 << i); - } - } - return VertexSizeForAttributes(mask); -} - -int KRMesh::getBoneCount() -{ - pack_header *header = getHeader(); - int bone_count = header->bone_count; - return bone_count; -} - -char *KRMesh::getBoneName(int bone_index) -{ - return getBone(bone_index)->szName; -} - -Matrix4 KRMesh::getBoneBindPose(int bone_index) -{ - return Matrix4(getBone(bone_index)->bind_pose); -} - -KRMesh::model_format_t KRMesh::getModelFormat() const -{ - model_format_t f = (model_format_t)getHeader()->model_format; - return f; -} - -bool KRMesh::rayCast(const Vector3 &start, const Vector3 &dir, const Triangle3 &tri, const Vector3 &tri_n0, const Vector3 &tri_n1, const Vector3 &tri_n2, HitInfo &hitinfo) -{ - Vector3 hit_point; - if(tri.rayCast(start, dir, hit_point)) { - // ---===--- hit_point is in triangle ---===--- - - float new_hit_distance = (hit_point - start).magnitude(); - if(new_hit_distance < hitinfo.getDistance() || !hitinfo.didHit()) { - // Update the hitinfo object if this hit is closer than the prior hit - - // Interpolate between the three vertex normals, performing a 3-way lerp of tri_n0, tri_n1, and tri_n2 - float distance_v0 = (tri[0] - hit_point).magnitude(); - float distance_v1 = (tri[1] - hit_point).magnitude(); - float distance_v2 = (tri[2] - hit_point).magnitude(); - float distance_total = distance_v0 + distance_v1 + distance_v2; - distance_v0 /= distance_total; - distance_v1 /= distance_total; - distance_v2 /= distance_total; - Vector3 normal = Vector3::Normalize(tri_n0 * (1.0 - distance_v0) + tri_n1 * (1.0 - distance_v1) + tri_n2 * (1.0 - distance_v2)); - - hitinfo = HitInfo(hit_point, normal, new_hit_distance); - return true; - } else { - return false; // The hit was farther than an existing hit - } - - } else { - // Dit not hit the triangle - return false; - } - -} - - -bool KRMesh::rayCast(const Vector3 &start, const Vector3 &dir, HitInfo &hitinfo) const -{ - m_pData->lock(); - bool hit_found = false; - for(int submesh_index=0; submesh_index < getSubmeshCount(); submesh_index++) { -// int vertex_start = getSubmesh(submesh_index)->start_vertex; - int vertex_count = getVertexCount(submesh_index); - switch(getModelFormat()) { - case KRENGINE_MODEL_FORMAT_TRIANGLES: - case KRENGINE_MODEL_FORMAT_INDEXED_TRIANGLES: - for(int triangle_index=0; triangle_index < vertex_count / 3; triangle_index++) { - int tri_vert_index[3]; // FINDME, HACK! This is not very efficient for indexed collider meshes... - tri_vert_index[0] = getTriangleVertexIndex(submesh_index, triangle_index*3); - tri_vert_index[1] = getTriangleVertexIndex(submesh_index, triangle_index*3 + 1); - tri_vert_index[2] = getTriangleVertexIndex(submesh_index, triangle_index*3 + 2); - - Triangle3 tri = Triangle3(getVertexPosition(tri_vert_index[0]), getVertexPosition(tri_vert_index[1]), getVertexPosition(tri_vert_index[2])); - - if(rayCast(start, dir, tri, getVertexNormal(tri_vert_index[0]), getVertexNormal(tri_vert_index[1]), getVertexNormal(tri_vert_index[2]), hitinfo)) hit_found = true; - } - break; - /* - - NOTE: Not yet supported: - - case KRENGINE_MODEL_FORMAT_STRIP: - case KRENGINE_MODEL_FORMAT_INDEXED_STRIP: - for(int triangle_index=0; triangle_index < vertex_count - 2; triangle_index++) { - int tri_vert_index[3]; - tri_vert_index[0] = getTriangleVertexIndex(submesh_index, vertex_start + triangle_index*3); - tri_vert_index[1] = getTriangleVertexIndex(submesh_index, vertex_start + triangle_index*3 + 1); - tri_vert_index[2] = getTriangleVertexIndex(submesh_index, vertex_start + triangle_index*3 + 2); - - if(rayCast(v0, dir, getVertexPosition(vertex_start + triangle_index), getVertexPosition(vertex_start + triangle_index+1), getVertexPosition(vertex_start + triangle_index+2), getVertexNormal(vertex_start + triangle_index), getVertexNormal(vertex_start + triangle_index+1), getVertexNormal(vertex_start + triangle_index+2), hitinfo)) hit_found = true; - } - break; - */ - default: - break; - } - } - m_pData->unlock(); - return hit_found; -} - - -bool KRMesh::sphereCast(const Matrix4 &model_to_world, const Vector3 &v0, const Vector3 &v1, float radius, HitInfo &hitinfo) const -{ - m_pData->lock(); - - bool hit_found = false; - for(int submesh_index=0; submesh_index < getSubmeshCount(); submesh_index++) { - int vertex_count = getVertexCount(submesh_index); - switch(getModelFormat()) { - case KRENGINE_MODEL_FORMAT_TRIANGLES: - case KRENGINE_MODEL_FORMAT_INDEXED_TRIANGLES: - for(int triangle_index=0; triangle_index < vertex_count / 3; triangle_index++) { - int tri_vert_index[3]; // FINDME, HACK! This is not very efficient for indexed collider meshes... - tri_vert_index[0] = getTriangleVertexIndex(submesh_index, triangle_index*3); - tri_vert_index[1] = getTriangleVertexIndex(submesh_index, triangle_index*3 + 1); - tri_vert_index[2] = getTriangleVertexIndex(submesh_index, triangle_index*3 + 2); - - Triangle3 tri = Triangle3(getVertexPosition(tri_vert_index[0]), getVertexPosition(tri_vert_index[1]), getVertexPosition(tri_vert_index[2])); - - if(sphereCast(model_to_world, v0, v1, radius, tri, hitinfo)) hit_found = true; - - /* - Triangle3 tri2 = Triangle3(getVertexPosition(tri_vert_index[1]), getVertexPosition(tri_vert_index[0]), getVertexPosition(tri_vert_index[2])); - - if(sphereCast(model_to_world, v0, v1, radius, tri2, new_hitinfo)) hit_found = true; - */ - } - break; - /* - - NOTE: Not yet supported: - - case KRENGINE_MODEL_FORMAT_STRIP: - case KRENGINE_MODEL_FORMAT_INDEXED_STRIP: - for(int triangle_index=0; triangle_index < vertex_count - 2; triangle_index++) { - int tri_vert_index[3]; - tri_vert_index[0] = getTriangleVertexIndex(submesh_index, vertex_start + triangle_index*3); - tri_vert_index[1] = getTriangleVertexIndex(submesh_index, vertex_start + triangle_index*3 + 1); - tri_vert_index[2] = getTriangleVertexIndex(submesh_index, vertex_start + triangle_index*3 + 2); - - if(sphereCast(model_to_world, v0, v1, getVertexPosition(vertex_start + triangle_index), getVertexPosition(vertex_start + triangle_index+1), getVertexPosition(vertex_start + triangle_index+2), getVertexNormal(vertex_start + triangle_index), getVertexNormal(vertex_start + triangle_index+1), getVertexNormal(vertex_start + triangle_index+2), new_hitinfo)) hit_found = true; - } - break; - */ - default: - break; - } - } - m_pData->unlock(); - - return hit_found; -} - -bool KRMesh::sphereCast(const Matrix4 &model_to_world, const Vector3 &v0, const Vector3 &v1, float radius, const Triangle3 &tri, HitInfo &hitinfo) -{ - - Vector3 dir = Vector3::Normalize(v1 - v0); - Vector3 start = v0; - - Vector3 new_hit_point; - float new_hit_distance; - - Triangle3 world_tri = Triangle3(Matrix4::Dot(model_to_world, tri[0]), Matrix4::Dot(model_to_world, tri[1]), Matrix4::Dot(model_to_world, tri[2])); - - if(world_tri.sphereCast(start, dir, radius, new_hit_point, new_hit_distance)) { - if((!hitinfo.didHit() || hitinfo.getDistance() > new_hit_distance) && new_hit_distance <= (v1 - v0).magnitude()) { - - /* - // Interpolate between the three vertex normals, performing a 3-way lerp of tri_n0, tri_n1, and tri_n2 - float distance_v0 = (tri[0] - new_hit_point).magnitude(); - float distance_v1 = (tri[1] - new_hit_point).magnitude(); - float distance_v2 = (tri[2] - new_hit_point).magnitude(); - float distance_total = distance_v0 + distance_v1 + distance_v2; - distance_v0 /= distance_total; - distance_v1 /= distance_total; - distance_v2 /= distance_total; - Vector3 normal = Vector3::Normalize(Matrix4::DotNoTranslate(model_to_world, (tri_n0 * (1.0 - distance_v0) + tri_n1 * (1.0 - distance_v1) + tri_n2 * (1.0 - distance_v2)))); - */ - hitinfo = HitInfo(new_hit_point, world_tri.calculateNormal(), new_hit_distance); - return true; - } - } - - return false; -} - -bool KRMesh::lineCast(const Vector3 &v0, const Vector3 &v1, HitInfo &hitinfo) const -{ - m_pData->lock(); - HitInfo new_hitinfo; - Vector3 dir = Vector3::Normalize(v1 - v0); - if(rayCast(v0, dir, new_hitinfo)) { - if((new_hitinfo.getPosition() - v0).sqrMagnitude() <= (v1 - v0).sqrMagnitude()) { - // The hit was between v1 and v2 - hitinfo = new_hitinfo; - m_pData->unlock(); - return true; - } - } - m_pData->unlock(); - return false; // Either no hit, or the hit was beyond v1 -} - -void KRMesh::convertToIndexed() -{ - m_pData->lock(); - char *szKey = new char[m_vertex_size * 2 + 1]; - - // Convert model to indexed vertices, identying vertexes with identical attributes and optimizing order of trianges for best usage post-vertex-transform cache on GPU - int vertex_index_offset = 0; - int vertex_index_base_start_vertex = 0; - - mesh_info mi; - - int bone_count = getBoneCount(); - for(int bone_index=0; bone_index < bone_count; bone_index++) { - mi.bone_names.push_back(getBoneName(bone_index)); - mi.bone_bind_poses.push_back(getBoneBindPose(bone_index)); - } - - for(int submesh_index=0; submesh_index < getSubmeshCount(); submesh_index++) { - mi.material_names.push_back(getSubmesh(submesh_index)->szName); - - int vertexes_remaining = getVertexCount(submesh_index); - - int vertex_count = vertexes_remaining; - if(vertex_count > 0xffff) { - vertex_count = 0xffff; - } - - if(submesh_index == 0 || vertex_index_offset + vertex_count > 0xffff) { - mi.vertex_index_bases.push_back(std::pair((int)mi.vertex_indexes.size(), (int)mi.vertices.size())); - vertex_index_offset = 0; - vertex_index_base_start_vertex = mi.vertices.size(); - } - - mi.submesh_starts.push_back(mi.vertex_index_bases.size() - 1 + (vertex_index_offset << 16)); - mi.submesh_lengths.push_back(vertexes_remaining); - int source_index = getSubmesh(submesh_index)->start_vertex; - - - while(vertexes_remaining) { - - //typedef std::pair, std::vector > vertex_key_t; - typedef std::string vertex_key_t; - - unordered_map prev_indexes = unordered_map(); - - for(int i=0; i < vertex_count; i++) { - - Vector3 vertex_position = getVertexPosition(source_index); - Vector2 vertex_uva = getVertexUVA(source_index); - Vector2 vertex_uvb = getVertexUVB(source_index); - Vector3 vertex_normal = getVertexNormal(source_index); - Vector3 vertex_tangent = getVertexTangent(source_index); - std::vector vertex_bone_indexes; - if(has_vertex_attribute(KRENGINE_ATTRIB_BONEINDEXES)) { - vertex_bone_indexes.push_back(getBoneIndex(source_index, 0)); - vertex_bone_indexes.push_back(getBoneIndex(source_index, 1)); - vertex_bone_indexes.push_back(getBoneIndex(source_index, 2)); - vertex_bone_indexes.push_back(getBoneIndex(source_index, 3)); - } - std::vector vertex_bone_weights; - if(has_vertex_attribute(KRENGINE_ATTRIB_BONEWEIGHTS)) { - vertex_bone_weights.push_back(getBoneWeight(source_index, 0)); - vertex_bone_weights.push_back(getBoneWeight(source_index, 1)); - vertex_bone_weights.push_back(getBoneWeight(source_index, 2)); - vertex_bone_weights.push_back(getBoneWeight(source_index, 3)); - } - - - - unsigned char *vertex_data = (unsigned char *)getVertexData(source_index); - for(int b=0; b < m_vertex_size; b++) { - const char *szHex = "0123456789ABCDEF"; - szKey[b*2] = szHex[vertex_data[b] & 0x0f]; - szKey[b*2+1] = szHex[((vertex_data[b] & 0xf0) >> 4)]; - } - szKey[m_vertex_size * 2] = '\0'; - - vertex_key_t vertex_key = szKey; - /* - - vertex_key_t vertex_key = std::make_pair(std::vector(), std::vector()); - - if(has_vertex_attribute(KRENGINE_ATTRIB_VERTEX) || has_vertex_attribute(KRENGINE_ATTRIB_VERTEX_SHORT)) { - vertex_key.first.push_back(vertex_position.x); - vertex_key.first.push_back(vertex_position.y); - vertex_key.first.push_back(vertex_position.z); - } - if(has_vertex_attribute(KRENGINE_ATTRIB_NORMAL) || has_vertex_attribute(KRENGINE_ATTRIB_NORMAL_SHORT)) { - vertex_key.first.push_back(vertex_normal.x); - vertex_key.first.push_back(vertex_normal.y); - vertex_key.first.push_back(vertex_normal.z); - } - if(has_vertex_attribute(KRENGINE_ATTRIB_TEXUVA) || has_vertex_attribute(KRENGINE_ATTRIB_TEXUVA_SHORT)) { - vertex_key.first.push_back(vertex_uva.x); - vertex_key.first.push_back(vertex_uva.y); - } - if(has_vertex_attribute(KRENGINE_ATTRIB_TEXUVB) || has_vertex_attribute(KRENGINE_ATTRIB_TEXUVB_SHORT)) { - vertex_key.first.push_back(vertex_uvb.x); - vertex_key.first.push_back(vertex_uvb.y); - } - if(has_vertex_attribute(KRENGINE_ATTRIB_TANGENT) || has_vertex_attribute(KRENGINE_ATTRIB_TANGENT_SHORT)) { - vertex_key.first.push_back(vertex_tangent.x); - vertex_key.first.push_back(vertex_tangent.y); - vertex_key.first.push_back(vertex_tangent.z); - } - if(has_vertex_attribute(KRENGINE_ATTRIB_BONEINDEXES)) { - vertex_key.second.push_back(vertex_bone_indexes[0]); - vertex_key.second.push_back(vertex_bone_indexes[1]); - vertex_key.second.push_back(vertex_bone_indexes[2]); - vertex_key.second.push_back(vertex_bone_indexes[3]); - } - if(has_vertex_attribute(KRENGINE_ATTRIB_BONEWEIGHTS)) { - vertex_key.first.push_back(vertex_bone_weights[0]); - vertex_key.first.push_back(vertex_bone_weights[1]); - vertex_key.first.push_back(vertex_bone_weights[2]); - vertex_key.first.push_back(vertex_bone_weights[3]); - } - */ - int found_index = -1; - if(prev_indexes.count(vertex_key) == 0) { - found_index = mi.vertices.size() - vertex_index_base_start_vertex; - if(has_vertex_attribute(KRENGINE_ATTRIB_VERTEX) || has_vertex_attribute(KRENGINE_ATTRIB_VERTEX_SHORT)) { - mi.vertices.push_back(vertex_position); - } - if(has_vertex_attribute(KRENGINE_ATTRIB_NORMAL) || has_vertex_attribute(KRENGINE_ATTRIB_NORMAL_SHORT)) { - mi.normals.push_back(vertex_normal); - } - if(has_vertex_attribute(KRENGINE_ATTRIB_TANGENT) || has_vertex_attribute(KRENGINE_ATTRIB_TANGENT_SHORT)) { - mi.tangents.push_back(vertex_tangent); - } - if(has_vertex_attribute(KRENGINE_ATTRIB_TEXUVA) || has_vertex_attribute(KRENGINE_ATTRIB_TEXUVA_SHORT)) { - mi.uva.push_back(vertex_uva); - } - if(has_vertex_attribute(KRENGINE_ATTRIB_TEXUVB) || has_vertex_attribute(KRENGINE_ATTRIB_TEXUVB_SHORT)) { - mi.uvb.push_back(vertex_uvb); - } - if(has_vertex_attribute(KRENGINE_ATTRIB_BONEINDEXES)) { - mi.bone_indexes.push_back(vertex_bone_indexes); - - } - if(has_vertex_attribute(KRENGINE_ATTRIB_BONEWEIGHTS)) { - mi.bone_weights.push_back(vertex_bone_weights); - } - prev_indexes[vertex_key] = found_index; - } else { - found_index = prev_indexes[vertex_key]; - } - - mi.vertex_indexes.push_back(found_index); - //fprintf(stderr, "Submesh: %6i IndexBase: %3i Index: %6i\n", submesh_index, vertex_index_bases.size(), found_index); - - source_index++; - } - - vertexes_remaining -= vertex_count; - vertex_index_offset += vertex_count; - - - vertex_count = vertexes_remaining; - if(vertex_count > 0xffff) { - vertex_count = 0xffff; - } - - if(vertex_index_offset + vertex_count > 0xffff) { - mi.vertex_index_bases.push_back(std::pair((int)mi.vertex_indexes.size(), (int)mi.vertices.size())); - vertex_index_offset = 0; - vertex_index_base_start_vertex = mi.vertices.size(); - } - } - } - - delete[] szKey; - - KRContext::Log(KRContext::LOG_LEVEL_INFORMATION, "Convert to indexed, before: %i after: %i (%.2f%% saving)", getHeader()->vertex_count, mi.vertices.size(), ((float)getHeader()->vertex_count - (float)mi.vertices.size()) / (float)getHeader()->vertex_count * 100.0f); - - - mi.format = KRENGINE_MODEL_FORMAT_INDEXED_TRIANGLES; - - m_pData->unlock(); - LoadData(mi, false, false); -} - -void KRMesh::optimize() -{ - switch(getModelFormat()) { - case KRENGINE_MODEL_FORMAT_INDEXED_TRIANGLES: - optimizeIndexes(); - break; - default: - convertToIndexed(); // HACK, FINDME, TODO - This may not be ideal in every case and should be exposed through the API independently - break; - } -} - -void KRMesh::getIndexedRange(int index_group, int &start_index_offset, int &start_vertex_offset, int &index_count, int &vertex_count) const { - pack_header *h = getHeader(); - __uint32_t *index_base_data = getIndexBaseData(); - start_index_offset = index_base_data[index_group * 2]; - start_vertex_offset = index_base_data[index_group * 2 + 1]; - if(index_group + 1 < h->index_base_count) { - index_count = index_base_data[index_group * 2 + 2] - start_index_offset; - vertex_count = index_base_data[index_group * 2 + 3] - start_vertex_offset; - } else { - index_count = h->index_count - start_index_offset; - vertex_count = h->vertex_count - start_vertex_offset; - } -} - -int KRMesh::getTriangleVertexIndex(int submesh, int index) const -{ - switch(getModelFormat()) { - case KRENGINE_MODEL_FORMAT_INDEXED_TRIANGLES: - { - __uint16_t *index_data = getIndexData(); - - - int start_index_offset, start_vertex_offset, index_count, vertex_count; - int index_group = getSubmesh(submesh)->index_group; - int index_group_offset = getSubmesh(submesh)->index_group_offset; - int remaining_vertices = index_group_offset + index; - getIndexedRange(index_group++, start_index_offset, start_vertex_offset, index_count, vertex_count); - while(remaining_vertices >= index_count) { - remaining_vertices -= index_count; - getIndexedRange(index_group++, start_index_offset, start_vertex_offset, index_count, vertex_count); - } - return index_data[start_index_offset + remaining_vertices] + start_vertex_offset; - } - break; - default: - return getSubmesh(submesh)->start_vertex + index; - break; - } -} - -void KRMesh::optimizeIndexes() -{ - m_pData->lock(); - if(getModelFormat() == KRENGINE_MODEL_FORMAT_INDEXED_TRIANGLES) { - - __uint16_t *new_indices = (__uint16_t *)malloc(0x10000 * sizeof(__uint16_t)); - __uint16_t *vertex_mapping = (__uint16_t *)malloc(0x10000 * sizeof(__uint16_t)); - unsigned char *new_vertex_data = (unsigned char *)malloc(m_vertex_size * 0x10000); - - // FINDME, TODO, HACK - This will segfault if the KRData object is still mmap'ed to a read-only file. Need to detach from the file before calling this function. Currently, this function is only being used during the import process, so it isn't going to cause any problems for now. - - pack_header *header = getHeader(); - - __uint16_t *index_data = getIndexData(); - // unsigned char *vertex_data = getVertexData(); // Uncomment when re-enabling Step 2 below - - for(int submesh_index=0; submesh_index < header->submesh_count; submesh_index++) { - pack_material *submesh = getSubmesh(submesh_index); - int vertexes_remaining = submesh->vertex_count; - int index_group = getSubmesh(submesh_index)->index_group; - int index_group_offset = getSubmesh(submesh_index)->index_group_offset; - while(vertexes_remaining > 0) { - int start_index_offset, start_vertex_offset, index_count, vertex_count; - getIndexedRange(index_group++, start_index_offset, start_vertex_offset, index_count, vertex_count); - - int vertexes_to_process = vertexes_remaining; - if(vertexes_to_process + index_group_offset > 0xffff) { - vertexes_to_process = 0xffff - index_group_offset; - } - - __uint16_t *index_data_start = index_data + start_index_offset + index_group_offset; - - - // ----====---- Step 1: Optimize triangle drawing order to maximize use of the GPU's post-transform vertex cache ----====---- - Forsyth::OptimizeFaces(index_data_start, vertexes_to_process, vertex_count, new_indices, 16); // FINDME, TODO - GPU post-transform vertex cache size of 16 should be configureable - memcpy(index_data_start, new_indices, vertexes_to_process * sizeof(__uint16_t)); - vertexes_remaining -= vertexes_to_process; - - /* - - unsigned char * vertex_data_start = vertex_data + start_vertex_offset; - - // ----====---- Step 2: Re-order the vertex data to maintain cache coherency ----====---- - for(int i=0; i < vertex_count; i++) { - vertex_mapping[i] = i; - } - int new_vertex_index=0; - for(int index_number=0; index_number new_vertex_index) { - // Swap prev_vertex_index and new_vertex_index - - for(int i=0; i < index_count; i++) { - if(index_data_start[i] == prev_vertex_index) { - index_data_start[i] = new_vertex_index; - } else if(index_data_start[i] == new_vertex_index) { - index_data_start[i] = prev_vertex_index; - } - } - - int tmp = vertex_mapping[prev_vertex_index]; - vertex_mapping[prev_vertex_index] = vertex_mapping[new_vertex_index]; - vertex_mapping[new_vertex_index] = tmp; - - - new_vertex_index++; - } - } - - for(int i=0; i < vertex_count; i++) { - memcpy(new_vertex_data + vertex_mapping[i] * m_vertex_size, vertex_data_start + i * m_vertex_size, m_vertex_size); - } - memcpy(vertex_data_start, new_vertex_data, vertex_count * m_vertex_size); - */ - - - - index_group_offset = 0; - } - } - - free(new_indices); - free(vertex_mapping); - free(new_vertex_data); - } // if(getModelFormat() == KRENGINE_MODEL_FORMAT_INDEXED_TRIANGLES) - - m_pData->unlock(); -} +// +// KRMesh.cpp +// KREngine +// +// Copyright 2012 Kearwood Gilbert. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other materials +// provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// The views and conclusions contained in the software and documentation are those of the +// authors and should not be interpreted as representing official policies, either expressed +// or implied, of Kearwood Gilbert. +// + + +#include "KREngine-common.h" + +#include "KRMesh.h" + +#include "KRShader.h" +#include "KRShaderManager.h" +#include "KRContext.h" +#include "../3rdparty/forsyth/forsyth.h" + +KRMesh::KRMesh(KRContext &context, std::string name) : KRResource(context, name) { + setName(name); + + m_hasTransparency = false; + m_pData = NULL; + m_pMetaData = NULL; + m_pIndexBaseData = NULL; + m_constant = false; +} + +KRMesh::KRMesh(KRContext &context, std::string name, KRDataBlock *data) : KRResource(context, name) { + setName(name); + + m_hasTransparency = false; + m_pData = NULL; + m_pMetaData = NULL; + m_pIndexBaseData = NULL; + m_constant = false; + + loadPack(data); +} + +void KRMesh::setName(const std::string name) { + m_lodCoverage = 100; + m_lodBaseName = name; + + size_t last_underscore_pos = name.find_last_of('_'); + if(last_underscore_pos != std::string::npos) { + // Found an underscore + std::string suffix = name.substr(last_underscore_pos + 1); + if(suffix.find("lod") == 0) { + std::string lod_level_string = suffix.substr(3); + char *end = NULL; + int c = (int)strtol(lod_level_string.c_str(), &end, 10); + if(c >= 0 && c <= 100 && *end == '\0') { + m_lodCoverage = c; + m_lodBaseName = name.substr(0, last_underscore_pos); + } + } + } + +} + +int KRMesh::GetLODCoverage(const std::string &name) +{ + int lod_coverage = 100; + size_t last_underscore_pos = name.find_last_of('_'); + if(last_underscore_pos != std::string::npos) { + // Found an underscore + std::string suffix = name.substr(last_underscore_pos + 1); + if(suffix.find("lod") == 0) { + std::string lod_level_string = suffix.substr(3); + char *end = NULL; + int c = (int)strtol(lod_level_string.c_str(), &end, 10); + if(c >= 0 && c <= 100 && *end == '\0') { + lod_coverage = c; + //m_lodBaseName = name.substr(0, last_underscore_pos); + } + } + } + return lod_coverage; +} + + + +KRMesh::~KRMesh() { + releaseData(); +} + +void KRMesh::releaseData() { + m_hasTransparency = false; + m_submeshes.clear(); + if(m_pIndexBaseData) { + m_pIndexBaseData->unlock(); + delete m_pIndexBaseData; + m_pIndexBaseData = NULL; + } + if(m_pMetaData) { + m_pMetaData->unlock(); + delete m_pMetaData; + m_pMetaData = NULL; + } + if(m_pData) { + delete m_pData; + m_pData = NULL; + } +} + +std::string KRMesh::getExtension() { + return "krmesh"; +} + +bool KRMesh::save(const std::string& path) { + return m_pData->save(path); +} + +bool KRMesh::save(KRDataBlock &data) { + data.append(*m_pData); + return true; +} + +void KRMesh::loadPack(KRDataBlock *data) { + releaseData(); + + m_pData = data; + + pack_header ph; + m_pData->copy((void *)&ph, 0, sizeof(ph)); + m_pMetaData = m_pData->getSubBlock(0, sizeof(pack_header) + sizeof(pack_material) * ph.submesh_count + sizeof(pack_bone) * ph.bone_count); + m_pMetaData->lock(); + + m_pIndexBaseData = m_pData->getSubBlock(sizeof(pack_header) + sizeof(pack_material) * ph.submesh_count + sizeof(pack_bone) * ph.bone_count + KRALIGN(2 * ph.index_count), ph.index_base_count * 8); + m_pIndexBaseData->lock(); + + m_minPoint = Vector3::Create(ph.minx, ph.miny, ph.minz); + m_maxPoint = Vector3::Create(ph.maxx, ph.maxy, ph.maxz); + + updateAttributeOffsets(); +} + +void KRMesh::getMaterials() +{ + if(m_materials.size() == 0) { + + for(std::vector::iterator itr = m_submeshes.begin(); itr != m_submeshes.end(); itr++) { + const char *szMaterialName = (*itr)->szMaterialName; + KRMaterial *pMaterial = nullptr; + if(*szMaterialName != '\0') { + pMaterial = getContext().getMaterialManager()->getMaterial(szMaterialName); + } + m_materials.push_back(pMaterial); + if(pMaterial) { + m_uniqueMaterials.insert(pMaterial); + } else if(*szMaterialName != '\0') { + KRContext::Log(KRContext::LOG_LEVEL_WARNING, "Missing material: %s", szMaterialName); + } + } + + m_hasTransparency = false; + for(std::set::iterator mat_itr = m_uniqueMaterials.begin(); mat_itr != m_uniqueMaterials.end(); mat_itr++) { + if((*mat_itr)->isTransparent()) { + m_hasTransparency = true; + break; + } + } + } +} + +void KRMesh::preStream(float lodCoverage) +{ + getSubmeshes(); + getMaterials(); + + for(std::set::iterator mat_itr = m_uniqueMaterials.begin(); mat_itr != m_uniqueMaterials.end(); mat_itr++) { + (*mat_itr)->preStream(lodCoverage); + } + + int cSubmeshes = m_submeshes.size(); + for(int iSubmesh=0; iSubmeshvbo_data_blocks.begin(); vbo_data_itr != m_submeshes[iSubmesh]->vbo_data_blocks.end(); vbo_data_itr++) { + (*vbo_data_itr)->resetPoolExpiry(lodCoverage); + } + } +} + +void KRMesh::load() +{ + // Load immediately into the GPU rather than passing through the streamer + getSubmeshes(); + getMaterials(); + + int cSubmeshes = m_submeshes.size(); + for(int iSubmesh=0; iSubmeshvbo_data_blocks.begin(); vbo_data_itr != m_submeshes[iSubmesh]->vbo_data_blocks.end(); vbo_data_itr++) { + (*vbo_data_itr)->resetPoolExpiry(1.0f); + (*vbo_data_itr)->load(); + } + } +} + +kraken_stream_level KRMesh::getStreamLevel() +{ + kraken_stream_level stream_level = kraken_stream_level::STREAM_LEVEL_IN_HQ; + getSubmeshes(); + getMaterials(); + + for(std::set::iterator mat_itr = m_uniqueMaterials.begin(); mat_itr != m_uniqueMaterials.end(); mat_itr++) { + stream_level = KRMIN(stream_level, (*mat_itr)->getStreamLevel()); + } + bool all_vbo_data_loaded = true; + bool vbo_data_loaded = false; + int cSubmeshes = m_submeshes.size(); + for(int iSubmesh=0; iSubmeshvbo_data_blocks.begin(); vbo_data_itr != m_submeshes[iSubmesh]->vbo_data_blocks.end(); vbo_data_itr++) { + if((*vbo_data_itr)->isVBOReady()) { + vbo_data_loaded = true; + } else { + all_vbo_data_loaded = false; + } + } + } + + if(!vbo_data_loaded || !all_vbo_data_loaded) { + stream_level = kraken_stream_level::STREAM_LEVEL_OUT; + } + + return stream_level; +} + +void KRMesh::render(const std::string &object_name, KRCamera *pCamera, std::vector &point_lights, std::vector &directional_lights, std::vector&spot_lights, const KRViewport &viewport, const Matrix4 &matModel, KRTexture *pLightMap, KRNode::RenderPass renderPass, const std::vector &bones, const Vector3 &rim_color, float rim_power, float lod_coverage) { + + //fprintf(stderr, "Rendering model: %s\n", m_name.c_str()); + if(renderPass != KRNode::RENDER_PASS_ADDITIVE_PARTICLES && renderPass != KRNode::RENDER_PASS_PARTICLE_OCCLUSION && renderPass != KRNode::RENDER_PASS_VOLUMETRIC_EFFECTS_ADDITIVE) { + preStream(lod_coverage); + if(getStreamLevel() == kraken_stream_level::STREAM_LEVEL_OUT) { + + } else { + + getSubmeshes(); + getMaterials(); + + int cSubmeshes = m_submeshes.size(); + if(renderPass == KRNode::RENDER_PASS_SHADOWMAP) { + for(int iSubmesh=0; iSubmeshisTransparent()) { + // Exclude transparent and semi-transparent meshes from shadow maps + renderSubmesh(iSubmesh, renderPass, object_name, pMaterial->getName(), lod_coverage); + } + } + + } + } else { + // Apply submeshes in per-material batches to reduce number of state changes + for(std::set::iterator mat_itr = m_uniqueMaterials.begin(); mat_itr != m_uniqueMaterials.end(); mat_itr++) { + for(int iSubmesh=0; iSubmeshisTransparent() && renderPass != KRNode::RENDER_PASS_FORWARD_TRANSPARENT) || (pMaterial->isTransparent() && renderPass == KRNode::RENDER_PASS_FORWARD_TRANSPARENT)) { + std::vector bone_bind_poses; + for(int i=0; i < bones.size(); i++) { + bone_bind_poses.push_back(getBoneBindPose(i)); + } + if(pMaterial->bind(pCamera, point_lights, directional_lights, spot_lights, bones, bone_bind_poses, viewport, matModel, pLightMap, renderPass, rim_color, rim_power, lod_coverage)) { + + switch(pMaterial->getAlphaMode()) { + case KRMaterial::KRMATERIAL_ALPHA_MODE_OPAQUE: // Non-transparent materials + case KRMaterial::KRMATERIAL_ALPHA_MODE_TEST: // Alpha in diffuse texture is interpreted as punch-through when < 0.5 + renderSubmesh(iSubmesh, renderPass, object_name, pMaterial->getName(), lod_coverage); + break; + case KRMaterial::KRMATERIAL_ALPHA_MODE_BLENDONESIDE: // Blended alpha with backface culling + renderSubmesh(iSubmesh, renderPass, object_name, pMaterial->getName(), lod_coverage); + break; + case KRMaterial::KRMATERIAL_ALPHA_MODE_BLENDTWOSIDE: // Blended alpha rendered in two passes. First pass renders backfaces; second pass renders frontfaces. + // Render back faces first + GLDEBUG(glCullFace(GL_FRONT)); + renderSubmesh(iSubmesh, renderPass, object_name, pMaterial->getName(), lod_coverage); + + // Render front faces second + GLDEBUG(glCullFace(GL_BACK)); + renderSubmesh(iSubmesh, renderPass, object_name, pMaterial->getName(), lod_coverage); + break; + } + } + } + } + } + } + } + } + } +} + +GLfloat KRMesh::getMaxDimension() { + GLfloat m = 0.0; + if(m_maxPoint.x - m_minPoint.x > m) m = m_maxPoint.x - m_minPoint.x; + if(m_maxPoint.y - m_minPoint.y > m) m = m_maxPoint.y - m_minPoint.y; + if(m_maxPoint.z - m_minPoint.z > m) m = m_maxPoint.z - m_minPoint.z; + return m; +} + +bool KRMesh::hasTransparency() { + return m_hasTransparency; +} + + +void KRMesh::getSubmeshes() { + if(m_submeshes.size() == 0) { + pack_header *pHeader = getHeader(); + pack_material *pPackMaterials = (pack_material *)(pHeader+1); + m_submeshes.clear(); + for(int iMaterial=0; iMaterial < pHeader->submesh_count; iMaterial++) { + pack_material *pPackMaterial = pPackMaterials + iMaterial; + + Submesh *pSubmesh = new Submesh(); + pSubmesh->start_vertex = pPackMaterial->start_vertex; + pSubmesh->vertex_count = pPackMaterial->vertex_count; + + strncpy(pSubmesh->szMaterialName, pPackMaterial->szName, KRENGINE_MAX_NAME_LENGTH); + pSubmesh->szMaterialName[KRENGINE_MAX_NAME_LENGTH-1] = '\0'; + //fprintf(stderr, "Submesh material: \"%s\"\n", pSubmesh->szMaterialName); + m_submeshes.push_back(pSubmesh); + } + createDataBlocks(m_constant ? KRMeshManager::KRVBOData::CONSTANT : KRMeshManager::KRVBOData::STREAMING); + } +} + +void KRMesh::createDataBlocks(KRMeshManager::KRVBOData::vbo_type t) +{ + int cSubmeshes = m_submeshes.size(); + for(int iSubmesh=0; iSubmesh < cSubmeshes; iSubmesh++) { + + Submesh *pSubmesh = m_submeshes[iSubmesh]; + int cVertexes = pSubmesh->vertex_count; + int vertex_data_offset = getVertexDataOffset(); + int index_data_offset = getIndexDataOffset(); + pack_header *pHeader = getHeader(); + int32_t vertex_attrib_flags = pHeader->vertex_attrib_flags; + int32_t vertex_count = pHeader->vertex_count; + + int vbo_index=0; + if(getModelFormat() == KRENGINE_MODEL_FORMAT_INDEXED_TRIANGLES) { + + int index_group = getSubmesh(iSubmesh)->index_group; + int index_group_offset = getSubmesh(iSubmesh)->index_group_offset; + while(cVertexes > 0) { + + int start_index_offset, start_vertex_offset, index_count, vertex_count; + getIndexedRange(index_group++, start_index_offset, start_vertex_offset, index_count, vertex_count); + + if(m_submeshes[iSubmesh]->vertex_data_blocks.size() <= vbo_index) { + KRDataBlock *vertex_data_block = m_pData->getSubBlock(vertex_data_offset + start_vertex_offset * m_vertex_size, vertex_count * m_vertex_size); + KRDataBlock *index_data_block = m_pData->getSubBlock(index_data_offset + start_index_offset * 2, index_count * 2); + KRMeshManager::KRVBOData *vbo_data_block = new KRMeshManager::KRVBOData(getContext().getMeshManager(), *vertex_data_block, *index_data_block, vertex_attrib_flags, true, t); + m_submeshes[iSubmesh]->vertex_data_blocks.push_back(vertex_data_block); + m_submeshes[iSubmesh]->index_data_blocks.push_back(index_data_block); + m_submeshes[iSubmesh]->vbo_data_blocks.push_back(vbo_data_block); + } + vbo_index++; + + int vertex_draw_count = cVertexes; + if(vertex_draw_count > index_count - index_group_offset) vertex_draw_count = index_count - index_group_offset; + + cVertexes -= vertex_draw_count; + index_group_offset = 0; + } + + } else { + int cBuffers = (vertex_count + MAX_VBO_SIZE - 1) / MAX_VBO_SIZE; + int iVertex = pSubmesh->start_vertex; + int iBuffer = iVertex / MAX_VBO_SIZE; + iVertex = iVertex % MAX_VBO_SIZE; + while(cVertexes > 0) { + GLsizei cBufferVertexes = iBuffer < cBuffers - 1 ? MAX_VBO_SIZE : vertex_count % MAX_VBO_SIZE; + int vertex_size = m_vertex_size; + + if(m_submeshes[iSubmesh]->vertex_data_blocks.size() <= vbo_index) { + KRDataBlock *index_data_block = NULL; + KRDataBlock *vertex_data_block = m_pData->getSubBlock(vertex_data_offset + iBuffer * MAX_VBO_SIZE * vertex_size, vertex_size * cBufferVertexes); + KRMeshManager::KRVBOData *vbo_data_block = new KRMeshManager::KRVBOData(getContext().getMeshManager(), *vertex_data_block, *index_data_block, vertex_attrib_flags, true, t); + m_submeshes[iSubmesh]->vertex_data_blocks.push_back(vertex_data_block); + m_submeshes[iSubmesh]->vbo_data_blocks.push_back(vbo_data_block); + } + vbo_index++; + + if(iVertex + cVertexes >= MAX_VBO_SIZE) { + assert(iVertex + (MAX_VBO_SIZE - iVertex) <= cBufferVertexes); + + cVertexes -= (MAX_VBO_SIZE - iVertex); + iVertex = 0; + iBuffer++; + } else { + assert(iVertex + cVertexes <= cBufferVertexes); + + cVertexes = 0; + } + + } + } + } +} + +void KRMesh::renderSubmesh(int iSubmesh, KRNode::RenderPass renderPass, const std::string &object_name, const std::string &material_name, float lodCoverage) { + getSubmeshes(); + + Submesh *pSubmesh = m_submeshes[iSubmesh]; + int cVertexes = pSubmesh->vertex_count; + + int vbo_index=0; + if(getModelFormat() == KRENGINE_MODEL_FORMAT_INDEXED_TRIANGLES) { + + int index_group = getSubmesh(iSubmesh)->index_group; + int index_group_offset = getSubmesh(iSubmesh)->index_group_offset; + while(cVertexes > 0) { + + int start_index_offset, start_vertex_offset, index_count, vertex_count; + getIndexedRange(index_group++, start_index_offset, start_vertex_offset, index_count, vertex_count); + + KRMeshManager::KRVBOData *vbo_data_block = m_submeshes[iSubmesh]->vbo_data_blocks[vbo_index++]; + assert(vbo_data_block->isVBOReady()); + + m_pContext->getMeshManager()->bindVBO(vbo_data_block, lodCoverage); + + + int vertex_draw_count = cVertexes; + if(vertex_draw_count > index_count - index_group_offset) vertex_draw_count = index_count - index_group_offset; + + glDrawElements(GL_TRIANGLES, vertex_draw_count, GL_UNSIGNED_SHORT, BUFFER_OFFSET(index_group_offset * 2)); + m_pContext->getMeshManager()->log_draw_call(renderPass, object_name, material_name, vertex_draw_count); + cVertexes -= vertex_draw_count; + index_group_offset = 0; + } + + } else { + int cBuffers = (cVertexes + MAX_VBO_SIZE - 1) / MAX_VBO_SIZE; + int iVertex = pSubmesh->start_vertex; + int iBuffer = iVertex / MAX_VBO_SIZE; + iVertex = iVertex % MAX_VBO_SIZE; + while(cVertexes > 0) { + GLsizei cBufferVertexes = iBuffer < cBuffers - 1 ? MAX_VBO_SIZE : cVertexes % MAX_VBO_SIZE; + + KRMeshManager::KRVBOData *vbo_data_block = m_submeshes[iSubmesh]->vbo_data_blocks[vbo_index++]; + assert(vbo_data_block->isVBOReady()); + m_pContext->getMeshManager()->bindVBO(vbo_data_block, lodCoverage); + + + if(iVertex + cVertexes >= MAX_VBO_SIZE) { + assert(iVertex + (MAX_VBO_SIZE - iVertex) <= cBufferVertexes); + switch (getModelFormat()) { + case KRENGINE_MODEL_FORMAT_TRIANGLES: + GLDEBUG(glDrawArrays(GL_TRIANGLES, iVertex, (MAX_VBO_SIZE - iVertex))); + break; + case KRENGINE_MODEL_FORMAT_STRIP: + GLDEBUG(glDrawArrays(GL_TRIANGLE_STRIP, iVertex, (MAX_VBO_SIZE - iVertex))); + break; + case KRENGINE_MODEL_FORMAT_INDEXED_TRIANGLES: + GLDEBUG(glDrawArrays(GL_TRIANGLES, iVertex, (MAX_VBO_SIZE - iVertex))); + break; + case KRENGINE_MODEL_FORMAT_INDEXED_STRIP: + GLDEBUG(glDrawArrays(GL_TRIANGLE_STRIP, iVertex, (MAX_VBO_SIZE - iVertex))); + break; + default: + break; + } + m_pContext->getMeshManager()->log_draw_call(renderPass, object_name, material_name, (MAX_VBO_SIZE - iVertex)); + + cVertexes -= (MAX_VBO_SIZE - iVertex); + iVertex = 0; + iBuffer++; + } else { + assert(iVertex + cVertexes <= cBufferVertexes); + + switch (getModelFormat()) { + case KRENGINE_MODEL_FORMAT_TRIANGLES: + GLDEBUG(glDrawArrays(GL_TRIANGLES, iVertex, cVertexes)); + break; + case KRENGINE_MODEL_FORMAT_STRIP: + GLDEBUG(glDrawArrays(GL_TRIANGLE_STRIP, iVertex, cVertexes)); + break; + default: + break; + } + m_pContext->getMeshManager()->log_draw_call(renderPass, object_name, material_name, cVertexes); + + cVertexes = 0; + } + + } + } +} + +void KRMesh::LoadData(const KRMesh::mesh_info &mi, bool calculate_normals, bool calculate_tangents) { + + releaseData(); + + // TODO, FINDME - These values should be passed as a parameter and set by GUI flags + bool use_short_vertexes = false; + bool use_short_normals = true; + bool use_short_tangents = true; + bool use_short_uva = true; + bool use_short_uvb = true; + + if(use_short_vertexes) { + for(std::vector::const_iterator itr=mi.vertices.begin(); itr != mi.vertices.end(); itr++) { + if(fabsf((*itr).x) > 1.0f || fabsf((*itr).y) > 1.0f || fabsf((*itr).z) > 1.0f) { + use_short_vertexes = false; + } + } + } + + if(use_short_uva) { + for(std::vector::const_iterator itr=mi.uva.begin(); itr != mi.uva.end(); itr++) { + if(fabsf((*itr).x) > 1.0f || fabsf((*itr).y) > 1.0f) { + use_short_uva = false; + } + } + } + + if(use_short_uvb) { + for(std::vector::const_iterator itr=mi.uvb.begin(); itr != mi.uvb.end(); itr++) { + if(fabsf((*itr).x) > 1.0f || fabsf((*itr).y) > 1.0f) { + use_short_uvb = false; + } + } + } + + __int32_t vertex_attrib_flags = 0; + if(mi.vertices.size()) { + if(use_short_vertexes) { + vertex_attrib_flags |= (1 << KRENGINE_ATTRIB_VERTEX_SHORT); + } else { + vertex_attrib_flags |= (1 << KRENGINE_ATTRIB_VERTEX); + } + } + if(mi.normals.size() || calculate_normals) { + if(use_short_normals) { + vertex_attrib_flags += (1 << KRENGINE_ATTRIB_NORMAL_SHORT); + } else { + vertex_attrib_flags += (1 << KRENGINE_ATTRIB_NORMAL); + } + } + if(mi.tangents.size() || calculate_tangents) { + if(use_short_tangents) { + vertex_attrib_flags += (1 << KRENGINE_ATTRIB_TANGENT_SHORT); + } else { + vertex_attrib_flags += (1 << KRENGINE_ATTRIB_TANGENT); + } + } + if(mi.uva.size()) { + if(use_short_uva) { + vertex_attrib_flags += (1 << KRENGINE_ATTRIB_TEXUVA_SHORT); + } else { + vertex_attrib_flags += (1 << KRENGINE_ATTRIB_TEXUVA); + } + } + if(mi.uvb.size()) { + if(use_short_uvb) { + vertex_attrib_flags += (1 << KRENGINE_ATTRIB_TEXUVB_SHORT); + } else { + vertex_attrib_flags += (1 << KRENGINE_ATTRIB_TEXUVB); + } + } + if(mi.bone_names.size()) { + vertex_attrib_flags += (1 << KRENGINE_ATTRIB_BONEINDEXES) + (1 << KRENGINE_ATTRIB_BONEWEIGHTS); + } + size_t vertex_size = VertexSizeForAttributes(vertex_attrib_flags); + size_t index_count = mi.vertex_indexes.size(); + size_t index_base_count = mi.vertex_index_bases.size(); + size_t submesh_count = mi.submesh_lengths.size(); + size_t vertex_count = mi.vertices.size(); + size_t bone_count = mi.bone_names.size(); + size_t new_file_size = sizeof(pack_header) + sizeof(pack_material) * submesh_count + sizeof(pack_bone) * bone_count + KRALIGN(2 * index_count) + KRALIGN(8 * index_base_count) + vertex_size * vertex_count; + m_pData = new KRDataBlock(); + m_pMetaData = m_pData; + m_pData->expand(new_file_size); + m_pData->lock(); + pack_header *pHeader = getHeader(); + memset(pHeader, 0, sizeof(pack_header)); + pHeader->vertex_attrib_flags = vertex_attrib_flags; + pHeader->submesh_count = (__int32_t)submesh_count; + pHeader->vertex_count = (__int32_t)vertex_count; + pHeader->bone_count = (__int32_t)bone_count; + pHeader->index_count = (__int32_t)index_count; + pHeader->index_base_count = (__int32_t)index_base_count; + pHeader->model_format = mi.format; + strcpy(pHeader->szTag, "KROBJPACK1.2 "); + updateAttributeOffsets(); + + pack_material *pPackMaterials = (pack_material *)(pHeader+1); + + for(int iMaterial=0; iMaterial < pHeader->submesh_count; iMaterial++) { + pack_material *pPackMaterial = pPackMaterials + iMaterial; + pPackMaterial->start_vertex = mi.submesh_starts[iMaterial]; + pPackMaterial->vertex_count = mi.submesh_lengths[iMaterial]; + memset(pPackMaterial->szName, 0, KRENGINE_MAX_NAME_LENGTH); + strncpy(pPackMaterial->szName, mi.material_names[iMaterial].c_str(), KRENGINE_MAX_NAME_LENGTH); + } + + for(int bone_index=0; bone_index < bone_count; bone_index++) { + pack_bone *bone = getBone(bone_index); + memset(bone->szName, 0, KRENGINE_MAX_NAME_LENGTH); + strncpy(bone->szName, mi.bone_names[bone_index].c_str(), KRENGINE_MAX_NAME_LENGTH); + memcpy(bone->bind_pose, mi.bone_bind_poses[bone_index].c, sizeof(float) * 16); + } + + bool bFirstVertex = true; + + memset(getVertexData(), 0, m_vertex_size * mi.vertices.size()); + for(int iVertex=0; iVertex < mi.vertices.size(); iVertex++) { + Vector3 source_vertex = mi.vertices[iVertex]; + setVertexPosition(iVertex, source_vertex); + if(mi.bone_names.size()) { + for(int bone_weight_index=0; bone_weight_index m_maxPoint.x) m_maxPoint.x = source_vertex.x; + if(source_vertex.y > m_maxPoint.y) m_maxPoint.y = source_vertex.y; + if(source_vertex.z > m_maxPoint.z) m_maxPoint.z = source_vertex.z; + } + if(mi.uva.size() > iVertex) { + setVertexUVA(iVertex, mi.uva[iVertex]); + } + if(mi.uvb.size() > iVertex) { + setVertexUVB(iVertex, mi.uvb[iVertex]); + } + if(mi.normals.size() > iVertex) { + setVertexNormal(iVertex, Vector3::Normalize(mi.normals[iVertex])); + } + if(mi.tangents.size() > iVertex) { + setVertexTangent(iVertex, Vector3::Normalize(mi.tangents[iVertex])); + } + } + + pHeader->minx = m_minPoint.x; + pHeader->miny = m_minPoint.y; + pHeader->minz = m_minPoint.z; + pHeader->maxx = m_maxPoint.x; + pHeader->maxy = m_maxPoint.y; + pHeader->maxz = m_maxPoint.z; + + + __uint16_t *index_data = getIndexData(); + for(std::vector<__uint16_t>::const_iterator itr=mi.vertex_indexes.begin(); itr != mi.vertex_indexes.end(); itr++) { + *index_data++ = *itr; + } + + __uint32_t *index_base_data = getIndexBaseData(); + for(std::vector >::const_iterator itr=mi.vertex_index_bases.begin(); itr != mi.vertex_index_bases.end(); itr++) { + *index_base_data++ = (*itr).first; + *index_base_data++ = (*itr).second; + } + + if(getModelFormat() == KRENGINE_MODEL_FORMAT_TRIANGLES) { + // Calculate missing surface normals and tangents + //cout << " Calculate surface normals and tangents\n"; + if(calculate_normals || calculate_tangents) { + // NOTE: This will not work properly if the vertices are already indexed + for(int iVertex=0; iVertex < mi.vertices.size(); iVertex+= 3) { + Vector3 p1 = getVertexPosition(iVertex); + Vector3 p2 = getVertexPosition(iVertex+1); + Vector3 p3 = getVertexPosition(iVertex+2); + Vector3 v1 = p2 - p1; + Vector3 v2 = p3 - p1; + + + // -- Calculate normal if missing -- + if(calculate_normals) { + Vector3 first_normal = getVertexNormal(iVertex); + if(first_normal.x == 0.0f && first_normal.y == 0.0f && first_normal.z == 0.0f) { + // Note - We don't take into consideration smoothing groups or smoothing angles when generating normals; all generated normals represent flat shaded polygons + Vector3 normal = Vector3::Cross(v1, v2); + + normal.normalize(); + setVertexNormal(iVertex, normal); + setVertexNormal(iVertex+1, normal); + setVertexNormal(iVertex+2, normal); + } + } + + // -- Calculate tangent vector for normal mapping -- + if(calculate_tangents) { + Vector3 first_tangent = getVertexTangent(iVertex); + if(first_tangent.x == 0.0f && first_tangent.y == 0.0f && first_tangent.z == 0.0f) { + + Vector2 uv0 = getVertexUVA(iVertex); + Vector2 uv1 = getVertexUVA(iVertex + 1); + Vector2 uv2 = getVertexUVA(iVertex + 2); + + Vector2 st1 = Vector2::Create(uv1.x - uv0.x, uv1.y - uv0.y); + Vector2 st2 = Vector2::Create(uv2.x - uv0.x, uv2.y - uv0.y); + double coef = 1/ (st1.x * st2.y - st2.x * st1.y); + + Vector3 tangent = Vector3::Create( + coef * ((v1.x * st2.y) + (v2.x * -st1.y)), + coef * ((v1.y * st2.y) + (v2.y * -st1.y)), + coef * ((v1.z * st2.y) + (v2.z * -st1.y)) + ); + + tangent.normalize(); + setVertexTangent(iVertex, tangent); + setVertexTangent(iVertex+1, tangent); + setVertexTangent(iVertex+2, tangent); + } + } + } + } + } + m_pData->unlock(); + + // ---- + + pack_header ph; + m_pData->copy((void *)&ph, 0, sizeof(ph)); + m_pMetaData = m_pData->getSubBlock(0, sizeof(pack_header) + sizeof(pack_material) * ph.submesh_count + sizeof(pack_bone) * ph.bone_count); + m_pMetaData->lock(); + m_pIndexBaseData = m_pData->getSubBlock(sizeof(pack_header) + sizeof(pack_material) * ph.submesh_count + sizeof(pack_bone) * ph.bone_count + KRALIGN(2 * ph.index_count), ph.index_base_count * 8); + m_pIndexBaseData->lock(); + + // ---- + + optimize(); +} + +Vector3 KRMesh::getMinPoint() const { + return m_minPoint; +} + +Vector3 KRMesh::getMaxPoint() const { + return m_maxPoint; +} + +int KRMesh::getLODCoverage() const { + return m_lodCoverage; +} + +std::string KRMesh::getLODBaseName() const { + return m_lodBaseName; +} + +// Predicate used with std::sort to sort by highest detail model first, decending to lowest detail LOD model +bool KRMesh::lod_sort_predicate(const KRMesh *m1, const KRMesh *m2) +{ + return m1->m_lodCoverage > m2->m_lodCoverage; +} + +bool KRMesh::has_vertex_attribute(vertex_attrib_t attribute_type) const +{ + //return (getHeader()->vertex_attrib_flags & (1 << attribute_type)) != 0; + return has_vertex_attribute(getHeader()->vertex_attrib_flags, attribute_type); +} + +bool KRMesh::has_vertex_attribute(int vertex_attrib_flags, vertex_attrib_t attribute_type) +{ + return (vertex_attrib_flags & (1 << attribute_type)) != 0; +} + +KRMesh::pack_header *KRMesh::getHeader() const +{ + return (pack_header *)m_pMetaData->getStart(); +} + +KRMesh::pack_bone *KRMesh::getBone(int index) +{ + pack_header *header = getHeader(); + return (pack_bone *)((unsigned char *)m_pMetaData->getStart() + sizeof(pack_header) + sizeof(pack_material) * header->submesh_count + sizeof(pack_bone) * index); +} + +unsigned char *KRMesh::getVertexData() const { + return ((unsigned char *)m_pData->getStart()) + getVertexDataOffset(); +} + +size_t KRMesh::getVertexDataOffset() const { + pack_header *pHeader = getHeader(); + return sizeof(pack_header) + sizeof(pack_material) * pHeader->submesh_count + sizeof(pack_bone) * pHeader->bone_count + KRALIGN(2 * pHeader->index_count) + KRALIGN(8 * pHeader->index_base_count); +} + +__uint16_t *KRMesh::getIndexData() const { + + return (__uint16_t *)((unsigned char *)m_pData->getStart() + getIndexDataOffset()); +} + +size_t KRMesh::getIndexDataOffset() const { + pack_header *pHeader = getHeader(); + return sizeof(pack_header) + sizeof(pack_material) * pHeader->submesh_count + sizeof(pack_bone) * pHeader->bone_count; +} + +__uint32_t *KRMesh::getIndexBaseData() const { + if(m_pIndexBaseData == NULL) { + pack_header *pHeader = getHeader(); + return (__uint32_t *)((unsigned char *)m_pData->getStart() + sizeof(pack_header) + sizeof(pack_material) * pHeader->submesh_count + sizeof(pack_bone) * pHeader->bone_count + KRALIGN(2 * pHeader->index_count)); + } else { + return (__uint32_t *)m_pIndexBaseData->getStart(); + } +} + + +KRMesh::pack_material *KRMesh::getSubmesh(int mesh_index) const +{ + return (pack_material *)((unsigned char *)m_pMetaData->getStart() + sizeof(pack_header)) + mesh_index; +} + +unsigned char *KRMesh::getVertexData(int index) const +{ + return getVertexData() + m_vertex_size * index; +} + +int KRMesh::getSubmeshCount() const +{ + pack_header *header = getHeader(); + int submesh_count = header->submesh_count; + return submesh_count; +} + +int KRMesh::getVertexCount(int submesh) const +{ + return getSubmesh(submesh)->vertex_count; +} + +Vector3 KRMesh::getVertexPosition(int index) const +{ + if(has_vertex_attribute(KRENGINE_ATTRIB_VERTEX_SHORT)) { + short *v = (short *)(getVertexData(index) + m_vertex_attribute_offset[KRENGINE_ATTRIB_VERTEX_SHORT]); + return Vector3::Create((float)v[0] / 32767.0f, (float)v[1] / 32767.0f, (float)v[2] / 32767.0f); + } else if(has_vertex_attribute(KRENGINE_ATTRIB_VERTEX)) { + return Vector3::Create((float *)(getVertexData(index) + m_vertex_attribute_offset[KRENGINE_ATTRIB_VERTEX])); + } else { + return Vector3::Zero(); + } +} + +Vector3 KRMesh::getVertexNormal(int index) const +{ + if(has_vertex_attribute(KRENGINE_ATTRIB_NORMAL_SHORT)) { + short *v = (short *)(getVertexData(index) + m_vertex_attribute_offset[KRENGINE_ATTRIB_NORMAL_SHORT]); + return Vector3::Create((float)v[0] / 32767.0f, (float)v[1] / 32767.0f, (float)v[2] / 32767.0f); + } else if(has_vertex_attribute(KRENGINE_ATTRIB_NORMAL)) { + return Vector3::Create((float *)(getVertexData(index) + m_vertex_attribute_offset[KRENGINE_ATTRIB_NORMAL])); + } else { + return Vector3::Zero(); + } +} + +Vector3 KRMesh::getVertexTangent(int index) const +{ + if(has_vertex_attribute(KRENGINE_ATTRIB_TANGENT_SHORT)) { + short *v = (short *)(getVertexData(index) + m_vertex_attribute_offset[KRENGINE_ATTRIB_TANGENT_SHORT]); + return Vector3::Create((float)v[0] / 32767.0f, (float)v[1] / 32767.0f, (float)v[2] / 32767.0f); + } else if(has_vertex_attribute(KRENGINE_ATTRIB_TANGENT)) { + return Vector3::Create((float *)(getVertexData(index) + m_vertex_attribute_offset[KRENGINE_ATTRIB_TANGENT])); + } else { + return Vector3::Zero(); + } +} + +Vector2 KRMesh::getVertexUVA(int index) const +{ + if(has_vertex_attribute(KRENGINE_ATTRIB_TEXUVA_SHORT)) { + short *v = (short *)(getVertexData(index) + m_vertex_attribute_offset[KRENGINE_ATTRIB_TEXUVA_SHORT]); + return Vector2::Create((float)v[0] / 32767.0f, (float)v[1] / 32767.0f); + } else if(has_vertex_attribute(KRENGINE_ATTRIB_TEXUVA)) { + return Vector2::Create((float *)(getVertexData(index) + m_vertex_attribute_offset[KRENGINE_ATTRIB_TEXUVA])); + } else { + return Vector2::Zero(); + } +} + +Vector2 KRMesh::getVertexUVB(int index) const +{ + if(has_vertex_attribute(KRENGINE_ATTRIB_TEXUVB_SHORT)) { + short *v = (short *)(getVertexData(index) + m_vertex_attribute_offset[KRENGINE_ATTRIB_TEXUVB_SHORT]); + return Vector2::Create((float)v[0] / 32767.0f, (float)v[1] / 32767.0f); + } else if(has_vertex_attribute(KRENGINE_ATTRIB_TEXUVB)) { + return Vector2::Create((float *)(getVertexData(index) + m_vertex_attribute_offset[KRENGINE_ATTRIB_TEXUVB])); + } else { + return Vector2::Zero(); + } +} + +void KRMesh::setVertexPosition(int index, const Vector3 &v) +{ + if(has_vertex_attribute(KRENGINE_ATTRIB_VERTEX_SHORT)) { + short *vert = (short *)(getVertexData(index) + m_vertex_attribute_offset[KRENGINE_ATTRIB_VERTEX_SHORT]); + vert[0] = v.x * 32767.0f; + vert[1] = v.y * 32767.0f; + vert[2] = v.z * 32767.0f; + } else if(has_vertex_attribute(KRENGINE_ATTRIB_VERTEX)) { + float *vert = (float *)(getVertexData(index) + m_vertex_attribute_offset[KRENGINE_ATTRIB_VERTEX]); + vert[0] = v.x; + vert[1] = v.y; + vert[2] = v.z; + } +} + +void KRMesh::setVertexNormal(int index, const Vector3 &v) +{ + if(has_vertex_attribute(KRENGINE_ATTRIB_NORMAL_SHORT)) { + short *vert = (short *)(getVertexData(index) + m_vertex_attribute_offset[KRENGINE_ATTRIB_NORMAL_SHORT]); + vert[0] = v.x * 32767.0f; + vert[1] = v.y * 32767.0f; + vert[2] = v.z * 32767.0f; + } else if(has_vertex_attribute(KRENGINE_ATTRIB_NORMAL)) { + float *vert = (float *)(getVertexData(index) + m_vertex_attribute_offset[KRENGINE_ATTRIB_NORMAL]); + vert[0] = v.x; + vert[1] = v.y; + vert[2] = v.z; + } +} + +void KRMesh::setVertexTangent(int index, const Vector3 & v) +{ + if(has_vertex_attribute(KRENGINE_ATTRIB_TANGENT_SHORT)) { + short *vert = (short *)(getVertexData(index) + m_vertex_attribute_offset[KRENGINE_ATTRIB_TANGENT_SHORT]); + vert[0] = v.x * 32767.0f; + vert[1] = v.y * 32767.0f; + vert[2] = v.z * 32767.0f; + } else if(has_vertex_attribute(KRENGINE_ATTRIB_TANGENT)) { + float *vert = (float *)(getVertexData(index) + m_vertex_attribute_offset[KRENGINE_ATTRIB_TANGENT]); + vert[0] = v.x; + vert[1] = v.y; + vert[2] = v.z; + } +} + +void KRMesh::setVertexUVA(int index, const Vector2 &v) +{ + if(has_vertex_attribute(KRENGINE_ATTRIB_TEXUVA_SHORT)) { + short *vert = (short *)(getVertexData(index) + m_vertex_attribute_offset[KRENGINE_ATTRIB_TEXUVA_SHORT]); + vert[0] = v.x * 32767.0f; + vert[1] = v.y * 32767.0f; + } else if(has_vertex_attribute(KRENGINE_ATTRIB_TEXUVA)) { + float *vert = (float *)(getVertexData(index) + m_vertex_attribute_offset[KRENGINE_ATTRIB_TEXUVA]); + vert[0] = v.x; + vert[1] = v.y; + } +} + +void KRMesh::setVertexUVB(int index, const Vector2 &v) +{ + if(has_vertex_attribute(KRENGINE_ATTRIB_TEXUVB_SHORT)) { + short *vert = (short *)(getVertexData(index) + m_vertex_attribute_offset[KRENGINE_ATTRIB_TEXUVB_SHORT]); + vert[0] = v.x * 32767.0f; + vert[1] = v.y * 32767.0f; + } else if(has_vertex_attribute(KRENGINE_ATTRIB_TEXUVB)) { + float *vert = (float *)(getVertexData(index) + m_vertex_attribute_offset[KRENGINE_ATTRIB_TEXUVB]); + vert[0] = v.x; + vert[1] = v.y; + } +} + + +int KRMesh::getBoneIndex(int index, int weight_index) const +{ + unsigned char *vert = (unsigned char *)(getVertexData(index) + m_vertex_attribute_offset[KRENGINE_ATTRIB_BONEINDEXES]); + return vert[weight_index]; +} + +void KRMesh::setBoneIndex(int index, int weight_index, int bone_index) +{ + unsigned char *vert = (unsigned char *)(getVertexData(index) + m_vertex_attribute_offset[KRENGINE_ATTRIB_BONEINDEXES]); + vert[weight_index] = bone_index; +} + +float KRMesh::getBoneWeight(int index, int weight_index) const +{ + float *vert = (float *)(getVertexData(index) + m_vertex_attribute_offset[KRENGINE_ATTRIB_BONEWEIGHTS]); + return vert[weight_index]; +} + +void KRMesh::setBoneWeight(int index, int weight_index, float bone_weight) +{ + float *vert = (float *)(getVertexData(index) + m_vertex_attribute_offset[KRENGINE_ATTRIB_BONEWEIGHTS]); + vert[weight_index] = bone_weight; +} + +size_t KRMesh::VertexSizeForAttributes(__int32_t vertex_attrib_flags) +{ + size_t data_size = 0; + if(has_vertex_attribute(vertex_attrib_flags, KRENGINE_ATTRIB_VERTEX)) { + data_size += sizeof(float) * 3; + } + if(has_vertex_attribute(vertex_attrib_flags, KRENGINE_ATTRIB_NORMAL)) { + data_size += sizeof(float) * 3; + } + if(has_vertex_attribute(vertex_attrib_flags, KRENGINE_ATTRIB_TANGENT)) { + data_size += sizeof(float) * 3; + } + if(has_vertex_attribute(vertex_attrib_flags, KRENGINE_ATTRIB_TEXUVA)) { + data_size += sizeof(float) * 2; + } + if(has_vertex_attribute(vertex_attrib_flags, KRENGINE_ATTRIB_TEXUVB)) { + data_size += sizeof(float) * 2; + } + if(has_vertex_attribute(vertex_attrib_flags, KRENGINE_ATTRIB_BONEINDEXES)) { + data_size += 4; // 4 bytes + } + if(has_vertex_attribute(vertex_attrib_flags, KRENGINE_ATTRIB_BONEWEIGHTS)) { + data_size += sizeof(float) * 4; + } + if(has_vertex_attribute(vertex_attrib_flags, KRENGINE_ATTRIB_VERTEX_SHORT)) { + data_size += sizeof(short) * 4; // Extra short added in order to maintain 32-bit alignment. TODO, FINDME - Perhaps we can bind this as a vec4 and use the 4th component for another attribute... + } + if(has_vertex_attribute(vertex_attrib_flags, KRENGINE_ATTRIB_NORMAL_SHORT)) { + data_size += sizeof(short) * 4; // Extra short added in order to maintain 32-bit alignment. TODO, FINDME - Perhaps we can bind this as a vec4 and use the 4th component for another attribute... + } + if(has_vertex_attribute(vertex_attrib_flags, KRENGINE_ATTRIB_TANGENT_SHORT)) { + data_size += sizeof(short) * 4; // Extra short added in order to maintain 32-bit alignment. TODO, FINDME - Perhaps we can bind this as a vec4 and use the 4th component for another attribute... + } + if(has_vertex_attribute(vertex_attrib_flags, KRENGINE_ATTRIB_TEXUVA_SHORT)) { + data_size += sizeof(short) * 2; + } + if(has_vertex_attribute(vertex_attrib_flags, KRENGINE_ATTRIB_TEXUVB_SHORT)) { + data_size += sizeof(short) * 2; + } + return data_size; +} + +void KRMesh::updateAttributeOffsets() +{ + pack_header *header = getHeader(); + int mask = 0; + for(int i=0; i < KRENGINE_NUM_ATTRIBUTES; i++) { + if(has_vertex_attribute((vertex_attrib_t)i)) { + m_vertex_attribute_offset[i] = VertexSizeForAttributes(header->vertex_attrib_flags & mask); + } else { + m_vertex_attribute_offset[i] = -1; + } + mask = (mask << 1) | 1; + } + m_vertex_size = VertexSizeForAttributes(header->vertex_attrib_flags); +} + +size_t KRMesh::AttributeOffset(__int32_t vertex_attrib, __int32_t vertex_attrib_flags) +{ + int mask = 0; + for(int i=0; i < vertex_attrib; i++) { + if(vertex_attrib_flags & (1 << i)) { + mask |= (1 << i); + } + } + return VertexSizeForAttributes(mask); +} + +int KRMesh::getBoneCount() +{ + pack_header *header = getHeader(); + int bone_count = header->bone_count; + return bone_count; +} + +char *KRMesh::getBoneName(int bone_index) +{ + return getBone(bone_index)->szName; +} + +Matrix4 KRMesh::getBoneBindPose(int bone_index) +{ + return Matrix4::Create(getBone(bone_index)->bind_pose); +} + +KRMesh::model_format_t KRMesh::getModelFormat() const +{ + model_format_t f = (model_format_t)getHeader()->model_format; + return f; +} + +bool KRMesh::rayCast(const Vector3 &start, const Vector3 &dir, const Triangle3 &tri, const Vector3 &tri_n0, const Vector3 &tri_n1, const Vector3 &tri_n2, HitInfo &hitinfo) +{ + Vector3 hit_point; + if(tri.rayCast(start, dir, hit_point)) { + // ---===--- hit_point is in triangle ---===--- + + float new_hit_distance = (hit_point - start).magnitude(); + if(new_hit_distance < hitinfo.getDistance() || !hitinfo.didHit()) { + // Update the hitinfo object if this hit is closer than the prior hit + + // Interpolate between the three vertex normals, performing a 3-way lerp of tri_n0, tri_n1, and tri_n2 + float distance_v0 = (tri[0] - hit_point).magnitude(); + float distance_v1 = (tri[1] - hit_point).magnitude(); + float distance_v2 = (tri[2] - hit_point).magnitude(); + float distance_total = distance_v0 + distance_v1 + distance_v2; + distance_v0 /= distance_total; + distance_v1 /= distance_total; + distance_v2 /= distance_total; + Vector3 normal = Vector3::Normalize(tri_n0 * (1.0f - distance_v0) + tri_n1 * (1.0f - distance_v1) + tri_n2 * (1.0f - distance_v2)); + + hitinfo = HitInfo(hit_point, normal, new_hit_distance); + return true; + } else { + return false; // The hit was farther than an existing hit + } + + } else { + // Dit not hit the triangle + return false; + } + +} + + +bool KRMesh::rayCast(const Vector3 &start, const Vector3 &dir, HitInfo &hitinfo) const +{ + m_pData->lock(); + bool hit_found = false; + for(int submesh_index=0; submesh_index < getSubmeshCount(); submesh_index++) { +// int vertex_start = getSubmesh(submesh_index)->start_vertex; + int vertex_count = getVertexCount(submesh_index); + switch(getModelFormat()) { + case KRENGINE_MODEL_FORMAT_TRIANGLES: + case KRENGINE_MODEL_FORMAT_INDEXED_TRIANGLES: + for(int triangle_index=0; triangle_index < vertex_count / 3; triangle_index++) { + int tri_vert_index[3]; // FINDME, HACK! This is not very efficient for indexed collider meshes... + tri_vert_index[0] = getTriangleVertexIndex(submesh_index, triangle_index*3); + tri_vert_index[1] = getTriangleVertexIndex(submesh_index, triangle_index*3 + 1); + tri_vert_index[2] = getTriangleVertexIndex(submesh_index, triangle_index*3 + 2); + + Triangle3 tri = Triangle3::Create(getVertexPosition(tri_vert_index[0]), getVertexPosition(tri_vert_index[1]), getVertexPosition(tri_vert_index[2])); + + if(rayCast(start, dir, tri, getVertexNormal(tri_vert_index[0]), getVertexNormal(tri_vert_index[1]), getVertexNormal(tri_vert_index[2]), hitinfo)) hit_found = true; + } + break; + /* + + NOTE: Not yet supported: + + case KRENGINE_MODEL_FORMAT_STRIP: + case KRENGINE_MODEL_FORMAT_INDEXED_STRIP: + for(int triangle_index=0; triangle_index < vertex_count - 2; triangle_index++) { + int tri_vert_index[3]; + tri_vert_index[0] = getTriangleVertexIndex(submesh_index, vertex_start + triangle_index*3); + tri_vert_index[1] = getTriangleVertexIndex(submesh_index, vertex_start + triangle_index*3 + 1); + tri_vert_index[2] = getTriangleVertexIndex(submesh_index, vertex_start + triangle_index*3 + 2); + + if(rayCast(v0, dir, getVertexPosition(vertex_start + triangle_index), getVertexPosition(vertex_start + triangle_index+1), getVertexPosition(vertex_start + triangle_index+2), getVertexNormal(vertex_start + triangle_index), getVertexNormal(vertex_start + triangle_index+1), getVertexNormal(vertex_start + triangle_index+2), hitinfo)) hit_found = true; + } + break; + */ + default: + break; + } + } + m_pData->unlock(); + return hit_found; +} + + +bool KRMesh::sphereCast(const Matrix4 &model_to_world, const Vector3 &v0, const Vector3 &v1, float radius, HitInfo &hitinfo) const +{ + m_pData->lock(); + + bool hit_found = false; + for(int submesh_index=0; submesh_index < getSubmeshCount(); submesh_index++) { + int vertex_count = getVertexCount(submesh_index); + switch(getModelFormat()) { + case KRENGINE_MODEL_FORMAT_TRIANGLES: + case KRENGINE_MODEL_FORMAT_INDEXED_TRIANGLES: + for(int triangle_index=0; triangle_index < vertex_count / 3; triangle_index++) { + int tri_vert_index[3]; // FINDME, HACK! This is not very efficient for indexed collider meshes... + tri_vert_index[0] = getTriangleVertexIndex(submesh_index, triangle_index*3); + tri_vert_index[1] = getTriangleVertexIndex(submesh_index, triangle_index*3 + 1); + tri_vert_index[2] = getTriangleVertexIndex(submesh_index, triangle_index*3 + 2); + + Triangle3 tri = Triangle3::Create(getVertexPosition(tri_vert_index[0]), getVertexPosition(tri_vert_index[1]), getVertexPosition(tri_vert_index[2])); + + if(sphereCast(model_to_world, v0, v1, radius, tri, hitinfo)) hit_found = true; + + /* + Triangle3 tri2 = Triangle3(getVertexPosition(tri_vert_index[1]), getVertexPosition(tri_vert_index[0]), getVertexPosition(tri_vert_index[2])); + + if(sphereCast(model_to_world, v0, v1, radius, tri2, new_hitinfo)) hit_found = true; + */ + } + break; + /* + + NOTE: Not yet supported: + + case KRENGINE_MODEL_FORMAT_STRIP: + case KRENGINE_MODEL_FORMAT_INDEXED_STRIP: + for(int triangle_index=0; triangle_index < vertex_count - 2; triangle_index++) { + int tri_vert_index[3]; + tri_vert_index[0] = getTriangleVertexIndex(submesh_index, vertex_start + triangle_index*3); + tri_vert_index[1] = getTriangleVertexIndex(submesh_index, vertex_start + triangle_index*3 + 1); + tri_vert_index[2] = getTriangleVertexIndex(submesh_index, vertex_start + triangle_index*3 + 2); + + if(sphereCast(model_to_world, v0, v1, getVertexPosition(vertex_start + triangle_index), getVertexPosition(vertex_start + triangle_index+1), getVertexPosition(vertex_start + triangle_index+2), getVertexNormal(vertex_start + triangle_index), getVertexNormal(vertex_start + triangle_index+1), getVertexNormal(vertex_start + triangle_index+2), new_hitinfo)) hit_found = true; + } + break; + */ + default: + break; + } + } + m_pData->unlock(); + + return hit_found; +} + +bool KRMesh::sphereCast(const Matrix4 &model_to_world, const Vector3 &v0, const Vector3 &v1, float radius, const Triangle3 &tri, HitInfo &hitinfo) +{ + + Vector3 dir = Vector3::Normalize(v1 - v0); + Vector3 start = v0; + + Vector3 new_hit_point; + float new_hit_distance; + + Triangle3 world_tri = Triangle3::Create(Matrix4::Dot(model_to_world, tri[0]), Matrix4::Dot(model_to_world, tri[1]), Matrix4::Dot(model_to_world, tri[2])); + + if(world_tri.sphereCast(start, dir, radius, new_hit_point, new_hit_distance)) { + if((!hitinfo.didHit() || hitinfo.getDistance() > new_hit_distance) && new_hit_distance <= (v1 - v0).magnitude()) { + + /* + // Interpolate between the three vertex normals, performing a 3-way lerp of tri_n0, tri_n1, and tri_n2 + float distance_v0 = (tri[0] - new_hit_point).magnitude(); + float distance_v1 = (tri[1] - new_hit_point).magnitude(); + float distance_v2 = (tri[2] - new_hit_point).magnitude(); + float distance_total = distance_v0 + distance_v1 + distance_v2; + distance_v0 /= distance_total; + distance_v1 /= distance_total; + distance_v2 /= distance_total; + Vector3 normal = Vector3::Normalize(Matrix4::DotNoTranslate(model_to_world, (tri_n0 * (1.0 - distance_v0) + tri_n1 * (1.0 - distance_v1) + tri_n2 * (1.0 - distance_v2)))); + */ + hitinfo = HitInfo(new_hit_point, world_tri.calculateNormal(), new_hit_distance); + return true; + } + } + + return false; +} + +bool KRMesh::lineCast(const Vector3 &v0, const Vector3 &v1, HitInfo &hitinfo) const +{ + m_pData->lock(); + HitInfo new_hitinfo; + Vector3 dir = Vector3::Normalize(v1 - v0); + if(rayCast(v0, dir, new_hitinfo)) { + if((new_hitinfo.getPosition() - v0).sqrMagnitude() <= (v1 - v0).sqrMagnitude()) { + // The hit was between v1 and v2 + hitinfo = new_hitinfo; + m_pData->unlock(); + return true; + } + } + m_pData->unlock(); + return false; // Either no hit, or the hit was beyond v1 +} + +void KRMesh::convertToIndexed() +{ + m_pData->lock(); + char *szKey = new char[m_vertex_size * 2 + 1]; + + // Convert model to indexed vertices, identying vertexes with identical attributes and optimizing order of trianges for best usage post-vertex-transform cache on GPU + int vertex_index_offset = 0; + int vertex_index_base_start_vertex = 0; + + mesh_info mi; + + int bone_count = getBoneCount(); + for(int bone_index=0; bone_index < bone_count; bone_index++) { + mi.bone_names.push_back(getBoneName(bone_index)); + mi.bone_bind_poses.push_back(getBoneBindPose(bone_index)); + } + + for(int submesh_index=0; submesh_index < getSubmeshCount(); submesh_index++) { + mi.material_names.push_back(getSubmesh(submesh_index)->szName); + + int vertexes_remaining = getVertexCount(submesh_index); + + int vertex_count = vertexes_remaining; + if(vertex_count > 0xffff) { + vertex_count = 0xffff; + } + + if(submesh_index == 0 || vertex_index_offset + vertex_count > 0xffff) { + mi.vertex_index_bases.push_back(std::pair((int)mi.vertex_indexes.size(), (int)mi.vertices.size())); + vertex_index_offset = 0; + vertex_index_base_start_vertex = mi.vertices.size(); + } + + mi.submesh_starts.push_back(mi.vertex_index_bases.size() - 1 + (vertex_index_offset << 16)); + mi.submesh_lengths.push_back(vertexes_remaining); + int source_index = getSubmesh(submesh_index)->start_vertex; + + + while(vertexes_remaining) { + + //typedef std::pair, std::vector > vertex_key_t; + typedef std::string vertex_key_t; + + unordered_map prev_indexes = unordered_map(); + + for(int i=0; i < vertex_count; i++) { + + Vector3 vertex_position = getVertexPosition(source_index); + Vector2 vertex_uva = getVertexUVA(source_index); + Vector2 vertex_uvb = getVertexUVB(source_index); + Vector3 vertex_normal = getVertexNormal(source_index); + Vector3 vertex_tangent = getVertexTangent(source_index); + std::vector vertex_bone_indexes; + if(has_vertex_attribute(KRENGINE_ATTRIB_BONEINDEXES)) { + vertex_bone_indexes.push_back(getBoneIndex(source_index, 0)); + vertex_bone_indexes.push_back(getBoneIndex(source_index, 1)); + vertex_bone_indexes.push_back(getBoneIndex(source_index, 2)); + vertex_bone_indexes.push_back(getBoneIndex(source_index, 3)); + } + std::vector vertex_bone_weights; + if(has_vertex_attribute(KRENGINE_ATTRIB_BONEWEIGHTS)) { + vertex_bone_weights.push_back(getBoneWeight(source_index, 0)); + vertex_bone_weights.push_back(getBoneWeight(source_index, 1)); + vertex_bone_weights.push_back(getBoneWeight(source_index, 2)); + vertex_bone_weights.push_back(getBoneWeight(source_index, 3)); + } + + + + unsigned char *vertex_data = (unsigned char *)getVertexData(source_index); + for(int b=0; b < m_vertex_size; b++) { + const char *szHex = "0123456789ABCDEF"; + szKey[b*2] = szHex[vertex_data[b] & 0x0f]; + szKey[b*2+1] = szHex[((vertex_data[b] & 0xf0) >> 4)]; + } + szKey[m_vertex_size * 2] = '\0'; + + vertex_key_t vertex_key = szKey; + /* + + vertex_key_t vertex_key = std::make_pair(std::vector(), std::vector()); + + if(has_vertex_attribute(KRENGINE_ATTRIB_VERTEX) || has_vertex_attribute(KRENGINE_ATTRIB_VERTEX_SHORT)) { + vertex_key.first.push_back(vertex_position.x); + vertex_key.first.push_back(vertex_position.y); + vertex_key.first.push_back(vertex_position.z); + } + if(has_vertex_attribute(KRENGINE_ATTRIB_NORMAL) || has_vertex_attribute(KRENGINE_ATTRIB_NORMAL_SHORT)) { + vertex_key.first.push_back(vertex_normal.x); + vertex_key.first.push_back(vertex_normal.y); + vertex_key.first.push_back(vertex_normal.z); + } + if(has_vertex_attribute(KRENGINE_ATTRIB_TEXUVA) || has_vertex_attribute(KRENGINE_ATTRIB_TEXUVA_SHORT)) { + vertex_key.first.push_back(vertex_uva.x); + vertex_key.first.push_back(vertex_uva.y); + } + if(has_vertex_attribute(KRENGINE_ATTRIB_TEXUVB) || has_vertex_attribute(KRENGINE_ATTRIB_TEXUVB_SHORT)) { + vertex_key.first.push_back(vertex_uvb.x); + vertex_key.first.push_back(vertex_uvb.y); + } + if(has_vertex_attribute(KRENGINE_ATTRIB_TANGENT) || has_vertex_attribute(KRENGINE_ATTRIB_TANGENT_SHORT)) { + vertex_key.first.push_back(vertex_tangent.x); + vertex_key.first.push_back(vertex_tangent.y); + vertex_key.first.push_back(vertex_tangent.z); + } + if(has_vertex_attribute(KRENGINE_ATTRIB_BONEINDEXES)) { + vertex_key.second.push_back(vertex_bone_indexes[0]); + vertex_key.second.push_back(vertex_bone_indexes[1]); + vertex_key.second.push_back(vertex_bone_indexes[2]); + vertex_key.second.push_back(vertex_bone_indexes[3]); + } + if(has_vertex_attribute(KRENGINE_ATTRIB_BONEWEIGHTS)) { + vertex_key.first.push_back(vertex_bone_weights[0]); + vertex_key.first.push_back(vertex_bone_weights[1]); + vertex_key.first.push_back(vertex_bone_weights[2]); + vertex_key.first.push_back(vertex_bone_weights[3]); + } + */ + int found_index = -1; + if(prev_indexes.count(vertex_key) == 0) { + found_index = mi.vertices.size() - vertex_index_base_start_vertex; + if(has_vertex_attribute(KRENGINE_ATTRIB_VERTEX) || has_vertex_attribute(KRENGINE_ATTRIB_VERTEX_SHORT)) { + mi.vertices.push_back(vertex_position); + } + if(has_vertex_attribute(KRENGINE_ATTRIB_NORMAL) || has_vertex_attribute(KRENGINE_ATTRIB_NORMAL_SHORT)) { + mi.normals.push_back(vertex_normal); + } + if(has_vertex_attribute(KRENGINE_ATTRIB_TANGENT) || has_vertex_attribute(KRENGINE_ATTRIB_TANGENT_SHORT)) { + mi.tangents.push_back(vertex_tangent); + } + if(has_vertex_attribute(KRENGINE_ATTRIB_TEXUVA) || has_vertex_attribute(KRENGINE_ATTRIB_TEXUVA_SHORT)) { + mi.uva.push_back(vertex_uva); + } + if(has_vertex_attribute(KRENGINE_ATTRIB_TEXUVB) || has_vertex_attribute(KRENGINE_ATTRIB_TEXUVB_SHORT)) { + mi.uvb.push_back(vertex_uvb); + } + if(has_vertex_attribute(KRENGINE_ATTRIB_BONEINDEXES)) { + mi.bone_indexes.push_back(vertex_bone_indexes); + + } + if(has_vertex_attribute(KRENGINE_ATTRIB_BONEWEIGHTS)) { + mi.bone_weights.push_back(vertex_bone_weights); + } + prev_indexes[vertex_key] = found_index; + } else { + found_index = prev_indexes[vertex_key]; + } + + mi.vertex_indexes.push_back(found_index); + //fprintf(stderr, "Submesh: %6i IndexBase: %3i Index: %6i\n", submesh_index, vertex_index_bases.size(), found_index); + + source_index++; + } + + vertexes_remaining -= vertex_count; + vertex_index_offset += vertex_count; + + + vertex_count = vertexes_remaining; + if(vertex_count > 0xffff) { + vertex_count = 0xffff; + } + + if(vertex_index_offset + vertex_count > 0xffff) { + mi.vertex_index_bases.push_back(std::pair((int)mi.vertex_indexes.size(), (int)mi.vertices.size())); + vertex_index_offset = 0; + vertex_index_base_start_vertex = mi.vertices.size(); + } + } + } + + delete[] szKey; + + KRContext::Log(KRContext::LOG_LEVEL_INFORMATION, "Convert to indexed, before: %i after: %i (%.2f%% saving)", getHeader()->vertex_count, mi.vertices.size(), ((float)getHeader()->vertex_count - (float)mi.vertices.size()) / (float)getHeader()->vertex_count * 100.0f); + + + mi.format = KRENGINE_MODEL_FORMAT_INDEXED_TRIANGLES; + + m_pData->unlock(); + LoadData(mi, false, false); +} + +void KRMesh::optimize() +{ + switch(getModelFormat()) { + case KRENGINE_MODEL_FORMAT_INDEXED_TRIANGLES: + optimizeIndexes(); + break; + default: + convertToIndexed(); // HACK, FINDME, TODO - This may not be ideal in every case and should be exposed through the API independently + break; + } +} + +void KRMesh::getIndexedRange(int index_group, int &start_index_offset, int &start_vertex_offset, int &index_count, int &vertex_count) const { + pack_header *h = getHeader(); + __uint32_t *index_base_data = getIndexBaseData(); + start_index_offset = index_base_data[index_group * 2]; + start_vertex_offset = index_base_data[index_group * 2 + 1]; + if(index_group + 1 < h->index_base_count) { + index_count = index_base_data[index_group * 2 + 2] - start_index_offset; + vertex_count = index_base_data[index_group * 2 + 3] - start_vertex_offset; + } else { + index_count = h->index_count - start_index_offset; + vertex_count = h->vertex_count - start_vertex_offset; + } +} + +int KRMesh::getTriangleVertexIndex(int submesh, int index) const +{ + switch(getModelFormat()) { + case KRENGINE_MODEL_FORMAT_INDEXED_TRIANGLES: + { + __uint16_t *index_data = getIndexData(); + + + int start_index_offset, start_vertex_offset, index_count, vertex_count; + int index_group = getSubmesh(submesh)->index_group; + int index_group_offset = getSubmesh(submesh)->index_group_offset; + int remaining_vertices = index_group_offset + index; + getIndexedRange(index_group++, start_index_offset, start_vertex_offset, index_count, vertex_count); + while(remaining_vertices >= index_count) { + remaining_vertices -= index_count; + getIndexedRange(index_group++, start_index_offset, start_vertex_offset, index_count, vertex_count); + } + return index_data[start_index_offset + remaining_vertices] + start_vertex_offset; + } + break; + default: + return getSubmesh(submesh)->start_vertex + index; + break; + } +} + +void KRMesh::optimizeIndexes() +{ + m_pData->lock(); + if(getModelFormat() == KRENGINE_MODEL_FORMAT_INDEXED_TRIANGLES) { + + __uint16_t *new_indices = (__uint16_t *)malloc(0x10000 * sizeof(__uint16_t)); + __uint16_t *vertex_mapping = (__uint16_t *)malloc(0x10000 * sizeof(__uint16_t)); + unsigned char *new_vertex_data = (unsigned char *)malloc(m_vertex_size * 0x10000); + + // FINDME, TODO, HACK - This will segfault if the KRData object is still mmap'ed to a read-only file. Need to detach from the file before calling this function. Currently, this function is only being used during the import process, so it isn't going to cause any problems for now. + + pack_header *header = getHeader(); + + __uint16_t *index_data = getIndexData(); + // unsigned char *vertex_data = getVertexData(); // Uncomment when re-enabling Step 2 below + + for(int submesh_index=0; submesh_index < header->submesh_count; submesh_index++) { + pack_material *submesh = getSubmesh(submesh_index); + int vertexes_remaining = submesh->vertex_count; + int index_group = getSubmesh(submesh_index)->index_group; + int index_group_offset = getSubmesh(submesh_index)->index_group_offset; + while(vertexes_remaining > 0) { + int start_index_offset, start_vertex_offset, index_count, vertex_count; + getIndexedRange(index_group++, start_index_offset, start_vertex_offset, index_count, vertex_count); + + int vertexes_to_process = vertexes_remaining; + if(vertexes_to_process + index_group_offset > 0xffff) { + vertexes_to_process = 0xffff - index_group_offset; + } + + __uint16_t *index_data_start = index_data + start_index_offset + index_group_offset; + + + // ----====---- Step 1: Optimize triangle drawing order to maximize use of the GPU's post-transform vertex cache ----====---- + Forsyth::OptimizeFaces(index_data_start, vertexes_to_process, vertex_count, new_indices, 16); // FINDME, TODO - GPU post-transform vertex cache size of 16 should be configureable + memcpy(index_data_start, new_indices, vertexes_to_process * sizeof(__uint16_t)); + vertexes_remaining -= vertexes_to_process; + + /* + + unsigned char * vertex_data_start = vertex_data + start_vertex_offset; + + // ----====---- Step 2: Re-order the vertex data to maintain cache coherency ----====---- + for(int i=0; i < vertex_count; i++) { + vertex_mapping[i] = i; + } + int new_vertex_index=0; + for(int index_number=0; index_number new_vertex_index) { + // Swap prev_vertex_index and new_vertex_index + + for(int i=0; i < index_count; i++) { + if(index_data_start[i] == prev_vertex_index) { + index_data_start[i] = new_vertex_index; + } else if(index_data_start[i] == new_vertex_index) { + index_data_start[i] = prev_vertex_index; + } + } + + int tmp = vertex_mapping[prev_vertex_index]; + vertex_mapping[prev_vertex_index] = vertex_mapping[new_vertex_index]; + vertex_mapping[new_vertex_index] = tmp; + + + new_vertex_index++; + } + } + + for(int i=0; i < vertex_count; i++) { + memcpy(new_vertex_data + vertex_mapping[i] * m_vertex_size, vertex_data_start + i * m_vertex_size, m_vertex_size); + } + memcpy(vertex_data_start, new_vertex_data, vertex_count * m_vertex_size); + */ + + + + index_group_offset = 0; + } + } + + free(new_indices); + free(vertex_mapping); + free(new_vertex_data); + } // if(getModelFormat() == KRENGINE_MODEL_FORMAT_INDEXED_TRIANGLES) + + m_pData->unlock(); +} diff --git a/kraken/KRMesh.h b/kraken/KRMesh.h index a83217d..ebcac52 100755 --- a/kraken/KRMesh.h +++ b/kraken/KRMesh.h @@ -1,290 +1,292 @@ -// -// KRMesh.h -// KREngine -// -// Copyright 2012 Kearwood Gilbert. All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, are -// permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of -// conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list -// of conditions and the following disclaimer in the documentation and/or other materials -// provided with the distribution. -// -// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED -// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR -// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// The views and conclusions contained in the software and documentation are those of the -// authors and should not be interpreted as representing official policies, either expressed -// or implied, of Kearwood Gilbert. -// -#include "KREngine-common.h" - -#include "KRContext.h" -#include "KRBone.h" -#include "KRMeshManager.h" - -#include "KREngine-common.h" - -using namespace kraken; - -#define MAX_VBO_SIZE 65535 -#define KRENGINE_MAX_BONE_WEIGHTS_PER_VERTEX 4 -#define KRENGINE_MAX_NAME_LENGTH 256 -// MAX_VBO_SIZE must be divisible by 3 so triangles aren't split across VBO objects... - -#define BUFFER_OFFSET(i) ((char *)NULL + (i)) - -#ifndef KRMesh_I -#define KRMesh_I - -#include "KRMaterialManager.h" -#include "KRCamera.h" -#include "KRViewport.h" - -class KRMaterial; -class KRNode; - -class KRMesh : public KRResource { - -public: - - - - KRMesh(KRContext &context, std::string name, KRDataBlock *data); - KRMesh(KRContext &context, std::string name); - virtual ~KRMesh(); - - kraken_stream_level getStreamLevel(); - void preStream(float lodCoverage); - - bool hasTransparency(); - - typedef enum { - KRENGINE_ATTRIB_VERTEX = 0, - KRENGINE_ATTRIB_NORMAL, - KRENGINE_ATTRIB_TANGENT, - KRENGINE_ATTRIB_TEXUVA, - KRENGINE_ATTRIB_TEXUVB, - KRENGINE_ATTRIB_BONEINDEXES, - KRENGINE_ATTRIB_BONEWEIGHTS, - KRENGINE_ATTRIB_VERTEX_SHORT, - KRENGINE_ATTRIB_NORMAL_SHORT, - KRENGINE_ATTRIB_TANGENT_SHORT, - KRENGINE_ATTRIB_TEXUVA_SHORT, - KRENGINE_ATTRIB_TEXUVB_SHORT, - KRENGINE_NUM_ATTRIBUTES - } vertex_attrib_t; - - typedef enum { - KRENGINE_MODEL_FORMAT_TRIANGLES = 0, - KRENGINE_MODEL_FORMAT_STRIP, - KRENGINE_MODEL_FORMAT_INDEXED_TRIANGLES, - KRENGINE_MODEL_FORMAT_INDEXED_STRIP - } model_format_t; - - typedef struct { - model_format_t format; - std::vector vertices; - std::vector<__uint16_t> vertex_indexes; - std::vector > vertex_index_bases; - std::vector uva; - std::vector uvb; - std::vector normals; - std::vector tangents; - std::vector submesh_starts; - std::vector submesh_lengths; - std::vector material_names; - std::vector bone_names; - std::vector > bone_indexes; - std::vector bone_bind_poses; - std::vector > bone_weights; - } mesh_info; - - void render(const std::string &object_name, KRCamera *pCamera, std::vector &point_lights, std::vector &directional_lights, std::vector&spot_lights, const KRViewport &viewport, const Matrix4 &matModel, KRTexture *pLightMap, KRNode::RenderPass renderPass, const std::vector &bones, const Vector3 &rim_color, float rim_power, float lod_coverage = 0.0f); - - std::string m_lodBaseName; - - virtual std::string getExtension(); - virtual bool save(const std::string& path); - virtual bool save(KRDataBlock &data); - - void LoadData(const mesh_info &mi, bool calculate_normals, bool calculate_tangents); - void loadPack(KRDataBlock *data); - - void convertToIndexed(); - void optimize(); - void optimizeIndexes(); - - void renderSubmesh(int iSubmesh, KRNode::RenderPass renderPass, const std::string &object_name, const std::string &material_name, float lodCoverage); - - GLfloat getMaxDimension(); - - Vector3 getMinPoint() const; - Vector3 getMaxPoint() const; - - class Submesh { - public: - Submesh() {}; - ~Submesh() { - for(auto itr = vbo_data_blocks.begin(); itr != vbo_data_blocks.end(); itr++) { - delete (*itr); - } - for(auto itr = vertex_data_blocks.begin(); itr != vertex_data_blocks.end(); itr++) { - delete (*itr); - } - for(auto itr = index_data_blocks.begin(); itr != index_data_blocks.end(); itr++) { - delete (*itr); - } - }; - - GLint start_vertex; - GLsizei vertex_count; - char szMaterialName[KRENGINE_MAX_NAME_LENGTH]; - vector vertex_data_blocks; - vector index_data_blocks; - vector vbo_data_blocks; - }; - - typedef struct { - union { - struct { // For Indexed triangles / strips - uint16_t index_group; - uint16_t index_group_offset; - }; - int32_t start_vertex; // For non-indexed trigangles / strips - }; - int32_t vertex_count; - char szName[KRENGINE_MAX_NAME_LENGTH]; - } pack_material; - - typedef struct { - char szName[KRENGINE_MAX_NAME_LENGTH]; - float bind_pose[16]; - } pack_bone; - - int getLODCoverage() const; - std::string getLODBaseName() const; - - - static bool lod_sort_predicate(const KRMesh *m1, const KRMesh *m2); - bool has_vertex_attribute(vertex_attrib_t attribute_type) const; - static bool has_vertex_attribute(int vertex_attrib_flags, vertex_attrib_t attribute_type); - - int getSubmeshCount() const; - int getVertexCount(int submesh) const; - int getTriangleVertexIndex(int submesh, int index) const; - Vector3 getVertexPosition(int index) const; - Vector3 getVertexNormal(int index) const; - Vector3 getVertexTangent(int index) const; - Vector2 getVertexUVA(int index) const; - Vector2 getVertexUVB(int index) const; - int getBoneIndex(int index, int weight_index) const; - float getBoneWeight(int index, int weight_index) const; - - void setVertexPosition(int index, const Vector3 &v); - void setVertexNormal(int index, const Vector3 &v); - void setVertexTangent(int index, const Vector3 & v); - void setVertexUVA(int index, const Vector2 &v); - void setVertexUVB(int index, const Vector2 &v); - void setBoneIndex(int index, int weight_index, int bone_index); - void setBoneWeight(int index, int weight_index, float bone_weight); - - static size_t VertexSizeForAttributes(__int32_t vertex_attrib_flags); - static size_t AttributeOffset(__int32_t vertex_attrib, __int32_t vertex_attrib_flags); - - int getBoneCount(); - char *getBoneName(int bone_index); - Matrix4 getBoneBindPose(int bone_index); - - - model_format_t getModelFormat() const; - - bool lineCast(const Vector3 &v0, const Vector3 &v1, HitInfo &hitinfo) const; - bool rayCast(const Vector3 &v0, const Vector3 &dir, HitInfo &hitinfo) const; - bool sphereCast(const Matrix4 &model_to_world, const Vector3 &v0, const Vector3 &v1, float radius, HitInfo &hitinfo) const; - - static int GetLODCoverage(const std::string &name); - - void load(); // Load immediately into the GPU rather than passing through the streamer - -protected: - bool m_constant; // TRUE if this should be always loaded and should not be passed through the streamer - -private: - KRDataBlock *m_pData; - KRDataBlock *m_pMetaData; - KRDataBlock *m_pIndexBaseData; - - void getSubmeshes(); - void getMaterials(); - - static bool rayCast(const Vector3 &start, const Vector3 &dir, const Triangle3 &tri, const Vector3 &tri_n0, const Vector3 &tri_n1, const Vector3 &tri_n2, HitInfo &hitinfo); - static bool sphereCast(const Matrix4 &model_to_world, const Vector3 &v0, const Vector3 &v1, float radius, const Triangle3 &tri, HitInfo &hitinfo); - - int m_lodCoverage; // This LOD level is activated when the bounding box of the model will cover less than this percent of the screen (100 = highest detail model) - vector m_materials; - set m_uniqueMaterials; - - bool m_hasTransparency; - - - Vector3 m_minPoint, m_maxPoint; - - - - - typedef struct { - char szTag[16]; - int32_t model_format; // 0 == Triangle list, 1 == Triangle strips, 2 == Indexed triangle list, 3 == Indexed triangle strips, rest are reserved (model_format_t enum) - int32_t vertex_attrib_flags; - int32_t vertex_count; - int32_t submesh_count; - int32_t bone_count; - float minx, miny, minz, maxx, maxy, maxz; // Axis aligned bounding box, in model's coordinate space - int32_t index_count; - int32_t index_base_count; - unsigned char reserved[444]; // Pad out to 512 bytes - } pack_header; - - vector m_submeshes; - int m_vertex_attribute_offset[KRENGINE_NUM_ATTRIBUTES]; - int m_vertex_size; - void updateAttributeOffsets(); - - void setName(const std::string name); - - - - pack_material *getSubmesh(int mesh_index) const; - unsigned char *getVertexData() const; - size_t getVertexDataOffset() const; - unsigned char *getVertexData(int index) const; - __uint16_t *getIndexData() const; - size_t getIndexDataOffset() const; - __uint32_t *getIndexBaseData() const; - pack_header *getHeader() const; - pack_bone *getBone(int index); - - - void getIndexedRange(int index_group, int &start_index_offset, int &start_vertex_offset, int &index_count, int &vertex_count) const; - - void releaseData(); - - void createDataBlocks(KRMeshManager::KRVBOData::vbo_type t); - - -}; - - +// +// KRMesh.h +// KREngine +// +// Copyright 2012 Kearwood Gilbert. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other materials +// provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// The views and conclusions contained in the software and documentation are those of the +// authors and should not be interpreted as representing official policies, either expressed +// or implied, of Kearwood Gilbert. +// +#include "KREngine-common.h" + +#include "KRContext.h" +#include "KRBone.h" +#include "KRMeshManager.h" + +#include "KREngine-common.h" + +#include "hydra.h" + +using namespace kraken; + +#define MAX_VBO_SIZE 65535 +#define KRENGINE_MAX_BONE_WEIGHTS_PER_VERTEX 4 +#define KRENGINE_MAX_NAME_LENGTH 256 +// MAX_VBO_SIZE must be divisible by 3 so triangles aren't split across VBO objects... + +#define BUFFER_OFFSET(i) ((char *)NULL + (i)) + +#ifndef KRMesh_I +#define KRMesh_I + +#include "KRMaterialManager.h" +#include "KRCamera.h" +#include "KRViewport.h" + +class KRMaterial; +class KRNode; + +class KRMesh : public KRResource { + +public: + + + + KRMesh(KRContext &context, std::string name, KRDataBlock *data); + KRMesh(KRContext &context, std::string name); + virtual ~KRMesh(); + + kraken_stream_level getStreamLevel(); + void preStream(float lodCoverage); + + bool hasTransparency(); + + typedef enum { + KRENGINE_ATTRIB_VERTEX = 0, + KRENGINE_ATTRIB_NORMAL, + KRENGINE_ATTRIB_TANGENT, + KRENGINE_ATTRIB_TEXUVA, + KRENGINE_ATTRIB_TEXUVB, + KRENGINE_ATTRIB_BONEINDEXES, + KRENGINE_ATTRIB_BONEWEIGHTS, + KRENGINE_ATTRIB_VERTEX_SHORT, + KRENGINE_ATTRIB_NORMAL_SHORT, + KRENGINE_ATTRIB_TANGENT_SHORT, + KRENGINE_ATTRIB_TEXUVA_SHORT, + KRENGINE_ATTRIB_TEXUVB_SHORT, + KRENGINE_NUM_ATTRIBUTES + } vertex_attrib_t; + + typedef enum { + KRENGINE_MODEL_FORMAT_TRIANGLES = 0, + KRENGINE_MODEL_FORMAT_STRIP, + KRENGINE_MODEL_FORMAT_INDEXED_TRIANGLES, + KRENGINE_MODEL_FORMAT_INDEXED_STRIP + } model_format_t; + + typedef struct { + model_format_t format; + std::vector vertices; + std::vector<__uint16_t> vertex_indexes; + std::vector > vertex_index_bases; + std::vector uva; + std::vector uvb; + std::vector normals; + std::vector tangents; + std::vector submesh_starts; + std::vector submesh_lengths; + std::vector material_names; + std::vector bone_names; + std::vector > bone_indexes; + std::vector bone_bind_poses; + std::vector > bone_weights; + } mesh_info; + + void render(const std::string &object_name, KRCamera *pCamera, std::vector &point_lights, std::vector &directional_lights, std::vector&spot_lights, const KRViewport &viewport, const Matrix4 &matModel, KRTexture *pLightMap, KRNode::RenderPass renderPass, const std::vector &bones, const Vector3 &rim_color, float rim_power, float lod_coverage = 0.0f); + + std::string m_lodBaseName; + + virtual std::string getExtension(); + virtual bool save(const std::string& path); + virtual bool save(KRDataBlock &data); + + void LoadData(const mesh_info &mi, bool calculate_normals, bool calculate_tangents); + void loadPack(KRDataBlock *data); + + void convertToIndexed(); + void optimize(); + void optimizeIndexes(); + + void renderSubmesh(int iSubmesh, KRNode::RenderPass renderPass, const std::string &object_name, const std::string &material_name, float lodCoverage); + + GLfloat getMaxDimension(); + + Vector3 getMinPoint() const; + Vector3 getMaxPoint() const; + + class Submesh { + public: + Submesh() {}; + ~Submesh() { + for(auto itr = vbo_data_blocks.begin(); itr != vbo_data_blocks.end(); itr++) { + delete (*itr); + } + for(auto itr = vertex_data_blocks.begin(); itr != vertex_data_blocks.end(); itr++) { + delete (*itr); + } + for(auto itr = index_data_blocks.begin(); itr != index_data_blocks.end(); itr++) { + delete (*itr); + } + }; + + GLint start_vertex; + GLsizei vertex_count; + char szMaterialName[KRENGINE_MAX_NAME_LENGTH]; + vector vertex_data_blocks; + vector index_data_blocks; + vector vbo_data_blocks; + }; + + typedef struct { + union { + struct { // For Indexed triangles / strips + uint16_t index_group; + uint16_t index_group_offset; + }; + int32_t start_vertex; // For non-indexed trigangles / strips + }; + int32_t vertex_count; + char szName[KRENGINE_MAX_NAME_LENGTH]; + } pack_material; + + typedef struct { + char szName[KRENGINE_MAX_NAME_LENGTH]; + float bind_pose[16]; + } pack_bone; + + int getLODCoverage() const; + std::string getLODBaseName() const; + + + static bool lod_sort_predicate(const KRMesh *m1, const KRMesh *m2); + bool has_vertex_attribute(vertex_attrib_t attribute_type) const; + static bool has_vertex_attribute(int vertex_attrib_flags, vertex_attrib_t attribute_type); + + int getSubmeshCount() const; + int getVertexCount(int submesh) const; + int getTriangleVertexIndex(int submesh, int index) const; + Vector3 getVertexPosition(int index) const; + Vector3 getVertexNormal(int index) const; + Vector3 getVertexTangent(int index) const; + Vector2 getVertexUVA(int index) const; + Vector2 getVertexUVB(int index) const; + int getBoneIndex(int index, int weight_index) const; + float getBoneWeight(int index, int weight_index) const; + + void setVertexPosition(int index, const Vector3 &v); + void setVertexNormal(int index, const Vector3 &v); + void setVertexTangent(int index, const Vector3 & v); + void setVertexUVA(int index, const Vector2 &v); + void setVertexUVB(int index, const Vector2 &v); + void setBoneIndex(int index, int weight_index, int bone_index); + void setBoneWeight(int index, int weight_index, float bone_weight); + + static size_t VertexSizeForAttributes(__int32_t vertex_attrib_flags); + static size_t AttributeOffset(__int32_t vertex_attrib, __int32_t vertex_attrib_flags); + + int getBoneCount(); + char *getBoneName(int bone_index); + Matrix4 getBoneBindPose(int bone_index); + + + model_format_t getModelFormat() const; + + bool lineCast(const Vector3 &v0, const Vector3 &v1, HitInfo &hitinfo) const; + bool rayCast(const Vector3 &v0, const Vector3 &dir, HitInfo &hitinfo) const; + bool sphereCast(const Matrix4 &model_to_world, const Vector3 &v0, const Vector3 &v1, float radius, HitInfo &hitinfo) const; + + static int GetLODCoverage(const std::string &name); + + void load(); // Load immediately into the GPU rather than passing through the streamer + +protected: + bool m_constant; // TRUE if this should be always loaded and should not be passed through the streamer + +private: + KRDataBlock *m_pData; + KRDataBlock *m_pMetaData; + KRDataBlock *m_pIndexBaseData; + + void getSubmeshes(); + void getMaterials(); + + static bool rayCast(const Vector3 &start, const Vector3 &dir, const Triangle3 &tri, const Vector3 &tri_n0, const Vector3 &tri_n1, const Vector3 &tri_n2, HitInfo &hitinfo); + static bool sphereCast(const Matrix4 &model_to_world, const Vector3 &v0, const Vector3 &v1, float radius, const Triangle3 &tri, HitInfo &hitinfo); + + int m_lodCoverage; // This LOD level is activated when the bounding box of the model will cover less than this percent of the screen (100 = highest detail model) + vector m_materials; + set m_uniqueMaterials; + + bool m_hasTransparency; + + + Vector3 m_minPoint, m_maxPoint; + + + + + typedef struct { + char szTag[16]; + int32_t model_format; // 0 == Triangle list, 1 == Triangle strips, 2 == Indexed triangle list, 3 == Indexed triangle strips, rest are reserved (model_format_t enum) + int32_t vertex_attrib_flags; + int32_t vertex_count; + int32_t submesh_count; + int32_t bone_count; + float minx, miny, minz, maxx, maxy, maxz; // Axis aligned bounding box, in model's coordinate space + int32_t index_count; + int32_t index_base_count; + unsigned char reserved[444]; // Pad out to 512 bytes + } pack_header; + + vector m_submeshes; + int m_vertex_attribute_offset[KRENGINE_NUM_ATTRIBUTES]; + int m_vertex_size; + void updateAttributeOffsets(); + + void setName(const std::string name); + + + + pack_material *getSubmesh(int mesh_index) const; + unsigned char *getVertexData() const; + size_t getVertexDataOffset() const; + unsigned char *getVertexData(int index) const; + __uint16_t *getIndexData() const; + size_t getIndexDataOffset() const; + __uint32_t *getIndexBaseData() const; + pack_header *getHeader() const; + pack_bone *getBone(int index); + + + void getIndexedRange(int index_group, int &start_index_offset, int &start_vertex_offset, int &index_count, int &vertex_count) const; + + void releaseData(); + + void createDataBlocks(KRMeshManager::KRVBOData::vbo_type t); + + +}; + + #endif // KRMesh_I \ No newline at end of file diff --git a/kraken/KRMeshCube.cpp b/kraken/KRMeshCube.cpp index 87b7cb3..43fc4ae 100755 --- a/kraken/KRMeshCube.cpp +++ b/kraken/KRMeshCube.cpp @@ -1,69 +1,69 @@ -// -// KRMeshCube.cpp -// KREngine -// -// Copyright 2012 Kearwood Gilbert. All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, are -// permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of -// conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list -// of conditions and the following disclaimer in the documentation and/or other materials -// provided with the distribution. -// -// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED -// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR -// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// The views and conclusions contained in the software and documentation are those of the -// authors and should not be interpreted as representing official policies, either expressed -// or implied, of Kearwood Gilbert. -// - -#include "KRMeshCube.h" - - -KRMeshCube::KRMeshCube(KRContext &context) : KRMesh(context, "__cube") -{ - m_constant = true; - - KRMesh::mesh_info mi; - - mi.vertices.push_back(Vector3(1.0, 1.0, 1.0)); - mi.vertices.push_back(Vector3(-1.0, 1.0, 1.0)); - mi.vertices.push_back(Vector3(1.0,-1.0, 1.0)); - mi.vertices.push_back(Vector3(-1.0,-1.0, 1.0)); - mi.vertices.push_back(Vector3(-1.0,-1.0,-1.0)); - mi.vertices.push_back(Vector3(-1.0, 1.0, 1.0)); - mi.vertices.push_back(Vector3(-1.0, 1.0,-1.0)); - mi.vertices.push_back(Vector3(1.0, 1.0, 1.0)); - mi.vertices.push_back(Vector3(1.0, 1.0,-1.0)); - mi.vertices.push_back(Vector3(1.0,-1.0, 1.0)); - mi.vertices.push_back(Vector3(1.0,-1.0,-1.0)); - mi.vertices.push_back(Vector3(-1.0,-1.0,-1.0)); - mi.vertices.push_back(Vector3(1.0, 1.0,-1.0)); - mi.vertices.push_back(Vector3(-1.0, 1.0,-1.0)); - - - mi.submesh_starts.push_back(0); - mi.submesh_lengths.push_back(mi.vertices.size()); - mi.material_names.push_back(""); - mi.format = KRENGINE_MODEL_FORMAT_STRIP; - - - LoadData(mi, true, true); -} - -KRMeshCube::~KRMeshCube() -{ - -} +// +// KRMeshCube.cpp +// KREngine +// +// Copyright 2012 Kearwood Gilbert. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other materials +// provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// The views and conclusions contained in the software and documentation are those of the +// authors and should not be interpreted as representing official policies, either expressed +// or implied, of Kearwood Gilbert. +// + +#include "KRMeshCube.h" + + +KRMeshCube::KRMeshCube(KRContext &context) : KRMesh(context, "__cube") +{ + m_constant = true; + + KRMesh::mesh_info mi; + + mi.vertices.push_back(Vector3::Create(1.0, 1.0, 1.0)); + mi.vertices.push_back(Vector3::Create(-1.0, 1.0, 1.0)); + mi.vertices.push_back(Vector3::Create(1.0,-1.0, 1.0)); + mi.vertices.push_back(Vector3::Create(-1.0,-1.0, 1.0)); + mi.vertices.push_back(Vector3::Create(-1.0,-1.0,-1.0)); + mi.vertices.push_back(Vector3::Create(-1.0, 1.0, 1.0)); + mi.vertices.push_back(Vector3::Create(-1.0, 1.0,-1.0)); + mi.vertices.push_back(Vector3::Create(1.0, 1.0, 1.0)); + mi.vertices.push_back(Vector3::Create(1.0, 1.0,-1.0)); + mi.vertices.push_back(Vector3::Create(1.0,-1.0, 1.0)); + mi.vertices.push_back(Vector3::Create(1.0,-1.0,-1.0)); + mi.vertices.push_back(Vector3::Create(-1.0,-1.0,-1.0)); + mi.vertices.push_back(Vector3::Create(1.0, 1.0,-1.0)); + mi.vertices.push_back(Vector3::Create(-1.0, 1.0,-1.0)); + + + mi.submesh_starts.push_back(0); + mi.submesh_lengths.push_back(mi.vertices.size()); + mi.material_names.push_back(""); + mi.format = KRENGINE_MODEL_FORMAT_STRIP; + + + LoadData(mi, true, true); +} + +KRMeshCube::~KRMeshCube() +{ + +} diff --git a/kraken/KRMeshManager.h b/kraken/KRMeshManager.h index 3a65397..08bf50e 100755 --- a/kraken/KRMeshManager.h +++ b/kraken/KRMeshManager.h @@ -1,207 +1,207 @@ -// -// KRMeshManager.h -// KREngine -// -// Copyright 2012 Kearwood Gilbert. All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, are -// permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of -// conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list -// of conditions and the following disclaimer in the documentation and/or other materials -// provided with the distribution. -// -// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED -// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR -// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// The views and conclusions contained in the software and documentation are those of the -// authors and should not be interpreted as representing official policies, either expressed -// or implied, of Kearwood Gilbert. -// - -#ifndef KRMESHMANAGER_H -#define KRMESHMANAGER_H - -#include "KREngine-common.h" -#include "KRContextObject.h" -#include "KRDataBlock.h" -#include "KRNode.h" - -class KRContext; -class KRMesh; - -class KRMeshManager : public KRContextObject { -public: - static const int KRENGINE_MAX_VOLUMETRIC_PLANES=500; - static const int KRENGINE_MAX_RANDOM_PARTICLES=150000; - - KRMeshManager(KRContext &context); - virtual ~KRMeshManager(); - - void startFrame(float deltaTime); - void endFrame(float deltaTime); - void firstFrame(); - - KRMesh *loadModel(const char *szName, KRDataBlock *pData); - std::vector getModel(const char *szName); - void addModel(KRMesh *model); - - std::vector getModelNames(); - unordered_multimap &getModels(); - - class KRVBOData { - - public: - - typedef enum { - STREAMING, - CONSTANT, - TEMPORARY - } vbo_type; - - KRVBOData(); - KRVBOData(KRMeshManager *manager, KRDataBlock &data, KRDataBlock &index_data, int vertex_attrib_flags, bool static_vbo, vbo_type t); - void init(KRMeshManager *manager, KRDataBlock &data, KRDataBlock &index_data, int vertex_attrib_flags, bool static_vbo, vbo_type t); - ~KRVBOData(); - - - KRDataBlock *m_data; - KRDataBlock *m_index_data; - - bool isVBOLoaded() { return m_is_vbo_loaded; } - bool isVBOReady() { return m_is_vbo_ready; } - void load(); - void unload(); - void bind(); - - // Disable copy constructors - KRVBOData(const KRVBOData& o) = delete; - KRVBOData(KRVBOData& o) = delete; - - long getSize() { return m_size; } - - void resetPoolExpiry(float lodCoverage); - long getLastFrameUsed() { return m_last_frame_used; } - - vbo_type getType() { return m_type; } - - float getStreamPriority(); - - void _swapHandles(); - - private: - KRMeshManager *m_manager; - int m_vertex_attrib_flags; - GLuint m_vbo_handle; - GLuint m_vbo_handle_indexes; - GLuint m_vao_handle; - GLsizeiptr m_size; - - long m_last_frame_used; - float m_last_frame_max_lod_coverage; - vbo_type m_type; - bool m_static_vbo; - bool m_is_vbo_loaded; - bool m_is_vbo_ready; - }; - - void bindVBO(KRVBOData *vbo_data, float lodCoverage); - void bindVBO(KRDataBlock &data, KRDataBlock &index_data, int vertex_attrib_flags, bool static_vbo, float lodCoverage); - void unbindVBO(); - long getMemUsed(); - long getMemActive(); - - static void configureAttribs(__int32_t attributes); - - typedef struct { - GLfloat x; - GLfloat y; - GLfloat z; - } Vector3D; - - typedef struct { - GLfloat u; - GLfloat v; - } TexCoord; - - typedef struct { - Vector3D vertex; - TexCoord uva; - } RandomParticleVertexData; - - typedef struct { - Vector3D vertex; - } VolumetricLightingVertexData; - - - KRDataBlock &getRandomParticles(); - KRDataBlock &getVolumetricLightingVertexes(); - - - long getMemoryTransferedThisFrame(); - - int getActiveVBOCount(); - - struct draw_call_info { - KRNode::RenderPass pass; - char object_name[256]; - char material_name[256]; - int vertex_count; - }; - - void log_draw_call(KRNode::RenderPass pass, const std::string &object_name, const std::string &material_name, int vertex_count); - std::vector getDrawCalls(); - - - - KRVBOData KRENGINE_VBO_DATA_3D_CUBE_VERTICES; - KRVBOData KRENGINE_VBO_DATA_2D_SQUARE_VERTICES; - - void doStreaming(long &memoryRemaining, long &memoryRemainingThisFrame); - -private: - KRDataBlock KRENGINE_VBO_3D_CUBE_VERTICES, KRENGINE_VBO_3D_CUBE_INDEXES; - __int32_t KRENGINE_VBO_3D_CUBE_ATTRIBS; - KRDataBlock KRENGINE_VBO_2D_SQUARE_VERTICES, KRENGINE_VBO_2D_SQUARE_INDEXES; - __int32_t KRENGINE_VBO_2D_SQUARE_ATTRIBS; - - unordered_multimap m_models; // Multiple models with the same name/key may be inserted, representing multiple LOD levels of the model - - long m_vboMemUsed; - KRVBOData *m_currentVBO; - - unordered_map m_vbosActive; - std::vector > m_activeVBOs_streamer; - std::vector > m_activeVBOs_streamer_copy; - - KRDataBlock m_randomParticleVertexData; - KRDataBlock m_volumetricLightingVertexData; - - long m_memoryTransferredThisFrame; - - std::vector m_draw_calls; - bool m_draw_call_logging_enabled; - bool m_draw_call_log_used; - - bool m_first_frame; - - std::mutex m_streamerFenceMutex; - bool m_streamerComplete; - - void balanceVBOMemory(long &memoryRemaining, long &memoryRemainingThisFrame); - - void primeVBO(KRVBOData *vbo_data); - -}; - -#endif +// +// KRMeshManager.h +// KREngine +// +// Copyright 2012 Kearwood Gilbert. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other materials +// provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// The views and conclusions contained in the software and documentation are those of the +// authors and should not be interpreted as representing official policies, either expressed +// or implied, of Kearwood Gilbert. +// + +#ifndef KRMESHMANAGER_H +#define KRMESHMANAGER_H + +#include "KREngine-common.h" +#include "KRContextObject.h" +#include "KRDataBlock.h" +#include "KRNode.h" + +class KRContext; +class KRMesh; + +class KRMeshManager : public KRContextObject { +public: + static const int KRENGINE_MAX_VOLUMETRIC_PLANES=500; + static const int KRENGINE_MAX_RANDOM_PARTICLES=150000; + + KRMeshManager(KRContext &context); + virtual ~KRMeshManager(); + + void startFrame(float deltaTime); + void endFrame(float deltaTime); + void firstFrame(); + + KRMesh *loadModel(const char *szName, KRDataBlock *pData); + std::vector getModel(const char *szName); + void addModel(KRMesh *model); + + std::vector getModelNames(); + unordered_multimap &getModels(); + + class KRVBOData { + + public: + + typedef enum { + STREAMING, + CONSTANT, + TEMPORARY + } vbo_type; + + KRVBOData(); + KRVBOData(KRMeshManager *manager, KRDataBlock &data, KRDataBlock &index_data, int vertex_attrib_flags, bool static_vbo, vbo_type t); + void init(KRMeshManager *manager, KRDataBlock &data, KRDataBlock &index_data, int vertex_attrib_flags, bool static_vbo, vbo_type t); + ~KRVBOData(); + + + KRDataBlock *m_data; + KRDataBlock *m_index_data; + + bool isVBOLoaded() { return m_is_vbo_loaded; } + bool isVBOReady() { return m_is_vbo_ready; } + void load(); + void unload(); + void bind(); + + // Disable copy constructors + KRVBOData(const KRVBOData& o) = delete; + KRVBOData& operator=(const KRVBOData& o) = delete; + + long getSize() { return m_size; } + + void resetPoolExpiry(float lodCoverage); + long getLastFrameUsed() { return m_last_frame_used; } + + vbo_type getType() { return m_type; } + + float getStreamPriority(); + + void _swapHandles(); + + private: + KRMeshManager *m_manager; + int m_vertex_attrib_flags; + GLuint m_vbo_handle; + GLuint m_vbo_handle_indexes; + GLuint m_vao_handle; + GLsizeiptr m_size; + + long m_last_frame_used; + float m_last_frame_max_lod_coverage; + vbo_type m_type; + bool m_static_vbo; + bool m_is_vbo_loaded; + bool m_is_vbo_ready; + }; + + void bindVBO(KRVBOData *vbo_data, float lodCoverage); + void bindVBO(KRDataBlock &data, KRDataBlock &index_data, int vertex_attrib_flags, bool static_vbo, float lodCoverage); + void unbindVBO(); + long getMemUsed(); + long getMemActive(); + + static void configureAttribs(__int32_t attributes); + + typedef struct { + GLfloat x; + GLfloat y; + GLfloat z; + } Vector3D; + + typedef struct { + GLfloat u; + GLfloat v; + } TexCoord; + + typedef struct { + Vector3D vertex; + TexCoord uva; + } RandomParticleVertexData; + + typedef struct { + Vector3D vertex; + } VolumetricLightingVertexData; + + + KRDataBlock &getRandomParticles(); + KRDataBlock &getVolumetricLightingVertexes(); + + + long getMemoryTransferedThisFrame(); + + int getActiveVBOCount(); + + struct draw_call_info { + KRNode::RenderPass pass; + char object_name[256]; + char material_name[256]; + int vertex_count; + }; + + void log_draw_call(KRNode::RenderPass pass, const std::string &object_name, const std::string &material_name, int vertex_count); + std::vector getDrawCalls(); + + + + KRVBOData KRENGINE_VBO_DATA_3D_CUBE_VERTICES; + KRVBOData KRENGINE_VBO_DATA_2D_SQUARE_VERTICES; + + void doStreaming(long &memoryRemaining, long &memoryRemainingThisFrame); + +private: + KRDataBlock KRENGINE_VBO_3D_CUBE_VERTICES, KRENGINE_VBO_3D_CUBE_INDEXES; + __int32_t KRENGINE_VBO_3D_CUBE_ATTRIBS; + KRDataBlock KRENGINE_VBO_2D_SQUARE_VERTICES, KRENGINE_VBO_2D_SQUARE_INDEXES; + __int32_t KRENGINE_VBO_2D_SQUARE_ATTRIBS; + + unordered_multimap m_models; // Multiple models with the same name/key may be inserted, representing multiple LOD levels of the model + + long m_vboMemUsed; + KRVBOData *m_currentVBO; + + unordered_map m_vbosActive; + std::vector > m_activeVBOs_streamer; + std::vector > m_activeVBOs_streamer_copy; + + KRDataBlock m_randomParticleVertexData; + KRDataBlock m_volumetricLightingVertexData; + + long m_memoryTransferredThisFrame; + + std::vector m_draw_calls; + bool m_draw_call_logging_enabled; + bool m_draw_call_log_used; + + bool m_first_frame; + + std::mutex m_streamerFenceMutex; + bool m_streamerComplete; + + void balanceVBOMemory(long &memoryRemaining, long &memoryRemainingThisFrame); + + void primeVBO(KRVBOData *vbo_data); + +}; + +#endif diff --git a/kraken/KRMeshQuad.cpp b/kraken/KRMeshQuad.cpp index be1e173..a8d87eb 100755 --- a/kraken/KRMeshQuad.cpp +++ b/kraken/KRMeshQuad.cpp @@ -1,63 +1,63 @@ -// -// KRMeshQuad.cpp -// KREngine -// -// Copyright 2012 Kearwood Gilbert. All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, are -// permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of -// conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list -// of conditions and the following disclaimer in the documentation and/or other materials -// provided with the distribution. -// -// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED -// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR -// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// The views and conclusions contained in the software and documentation are those of the -// authors and should not be interpreted as representing official policies, either expressed -// or implied, of Kearwood Gilbert. -// - -#include "KRMeshQuad.h" - - -KRMeshQuad::KRMeshQuad(KRContext &context) : KRMesh(context, "__quad") -{ - m_constant = true; - - KRMesh::mesh_info mi; - - mi.vertices.push_back(Vector3(-1.0f, -1.0f, 0.0f)); - mi.vertices.push_back(Vector3(1.0f, -1.0f, 0.0f)); - mi.vertices.push_back(Vector3(-1.0f, 1.0f, 0.0f)); - mi.vertices.push_back(Vector3(1.0f, 1.0f, 0.0f)); - - mi.uva.push_back(Vector2(0.0f, 0.0f)); - mi.uva.push_back(Vector2(1.0f, 0.0f)); - mi.uva.push_back(Vector2(0.0f, 1.0f)); - mi.uva.push_back(Vector2(1.0f, 1.0f)); - - - mi.submesh_starts.push_back(0); - mi.submesh_lengths.push_back(mi.vertices.size()); - mi.material_names.push_back(""); - mi.format = KRENGINE_MODEL_FORMAT_STRIP; - - LoadData(mi, true, true); -} - -KRMeshQuad::~KRMeshQuad() -{ - -} +// +// KRMeshQuad.cpp +// KREngine +// +// Copyright 2012 Kearwood Gilbert. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other materials +// provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// The views and conclusions contained in the software and documentation are those of the +// authors and should not be interpreted as representing official policies, either expressed +// or implied, of Kearwood Gilbert. +// + +#include "KRMeshQuad.h" + + +KRMeshQuad::KRMeshQuad(KRContext &context) : KRMesh(context, "__quad") +{ + m_constant = true; + + KRMesh::mesh_info mi; + + mi.vertices.push_back(Vector3::Create(-1.0f, -1.0f, 0.0f)); + mi.vertices.push_back(Vector3::Create(1.0f, -1.0f, 0.0f)); + mi.vertices.push_back(Vector3::Create(-1.0f, 1.0f, 0.0f)); + mi.vertices.push_back(Vector3::Create(1.0f, 1.0f, 0.0f)); + + mi.uva.push_back(Vector2::Create(0.0f, 0.0f)); + mi.uva.push_back(Vector2::Create(1.0f, 0.0f)); + mi.uva.push_back(Vector2::Create(0.0f, 1.0f)); + mi.uva.push_back(Vector2::Create(1.0f, 1.0f)); + + + mi.submesh_starts.push_back(0); + mi.submesh_lengths.push_back(mi.vertices.size()); + mi.material_names.push_back(""); + mi.format = KRENGINE_MODEL_FORMAT_STRIP; + + LoadData(mi, true, true); +} + +KRMeshQuad::~KRMeshQuad() +{ + +} diff --git a/kraken/KRMeshSphere.cpp b/kraken/KRMeshSphere.cpp index 9e491f3..4de8587 100755 --- a/kraken/KRMeshSphere.cpp +++ b/kraken/KRMeshSphere.cpp @@ -1,135 +1,135 @@ -// -// KRMeshSphere.cpp -// KREngine -// -// Copyright 2012 Kearwood Gilbert. All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, are -// permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of -// conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list -// of conditions and the following disclaimer in the documentation and/or other materials -// provided with the distribution. -// -// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED -// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR -// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// The views and conclusions contained in the software and documentation are those of the -// authors and should not be interpreted as representing official policies, either expressed -// or implied, of Kearwood Gilbert. -// - -#include "KRMeshSphere.h" - - -KRMeshSphere::KRMeshSphere(KRContext &context) : KRMesh(context, "__sphere") -{ - m_constant = true; - - KRMesh::mesh_info mi; - - // Create a triangular facet approximation to a sphere - // Based on algorithm from Paul Bourke: http://paulbourke.net/miscellaneous/sphere_cylinder/ - - int iterations = 3; - int facet_count = pow(4, iterations) * 8; - - class Facet3 { - public: - Facet3() { - - } - ~Facet3() { - - } - Vector3 p1; - Vector3 p2; - Vector3 p3; - }; - - std::vector f = std::vector(facet_count); - - int i,it; - float a; - Vector3 p[6] = { - Vector3(0,0,1), - Vector3(0,0,-1), - Vector3(-1,-1,0), - Vector3(1,-1,0), - Vector3(1,1,0), - Vector3(-1,1,0) - }; - - Vector3 pa,pb,pc; - int nt = 0,ntold; - - /* Create the level 0 object */ - a = 1 / sqrt(2.0); - for (i=0;i<6;i++) { - p[i].x *= a; - p[i].y *= a; - } - f[0].p1 = p[0]; f[0].p2 = p[3]; f[0].p3 = p[4]; - f[1].p1 = p[0]; f[1].p2 = p[4]; f[1].p3 = p[5]; - f[2].p1 = p[0]; f[2].p2 = p[5]; f[2].p3 = p[2]; - f[3].p1 = p[0]; f[3].p2 = p[2]; f[3].p3 = p[3]; - f[4].p1 = p[1]; f[4].p2 = p[4]; f[4].p3 = p[3]; - f[5].p1 = p[1]; f[5].p2 = p[5]; f[5].p3 = p[4]; - f[6].p1 = p[1]; f[6].p2 = p[2]; f[6].p3 = p[5]; - f[7].p1 = p[1]; f[7].p2 = p[3]; f[7].p3 = p[2]; - nt = 8; - - /* Bisect each edge and move to the surface of a unit sphere */ - for (it=0;it f = std::vector(facet_count); + + int i,it; + float a; + Vector3 p[6] = { + Vector3::Create(0,0,1), + Vector3::Create(0,0,-1), + Vector3::Create(-1,-1,0), + Vector3::Create(1,-1,0), + Vector3::Create(1,1,0), + Vector3::Create(-1,1,0) + }; + + Vector3 pa,pb,pc; + int nt = 0,ntold; + + /* Create the level 0 object */ + a = 1 / sqrt(2.0); + for (i=0;i<6;i++) { + p[i].x *= a; + p[i].y *= a; + } + f[0].p1 = p[0]; f[0].p2 = p[3]; f[0].p3 = p[4]; + f[1].p1 = p[0]; f[1].p2 = p[4]; f[1].p3 = p[5]; + f[2].p1 = p[0]; f[2].p2 = p[5]; f[2].p3 = p[2]; + f[3].p1 = p[0]; f[3].p2 = p[2]; f[3].p3 = p[3]; + f[4].p1 = p[1]; f[4].p2 = p[4]; f[4].p3 = p[3]; + f[5].p1 = p[1]; f[5].p2 = p[5]; f[5].p3 = p[4]; + f[6].p1 = p[1]; f[6].p2 = p[2]; f[6].p3 = p[5]; + f[7].p1 = p[1]; f[7].p2 = p[3]; f[7].p3 = p[2]; + nt = 8; + + /* Bisect each edge and move to the surface of a unit sphere */ + for (it=0;itSetAttribute("mesh", m_model_name.c_str()); - e->SetAttribute("light_map", m_lightMap.c_str()); - e->SetAttribute("lod_min_coverage", m_min_lod_coverage); - e->SetAttribute("receives_shadow", m_receivesShadow ? "true" : "false"); - e->SetAttribute("faces_camera", m_faces_camera ? "true" : "false"); - kraken::setXMLAttribute("rim_color", e, m_rim_color, Vector3::Zero()); - e->SetAttribute("rim_power", m_rim_power); - return e; -} - -void KRModel::setRimColor(const Vector3 &rim_color) -{ - m_rim_color = rim_color; -} - -void KRModel::setRimPower(float rim_power) -{ - m_rim_power = rim_power; -} - -Vector3 KRModel::getRimColor() -{ - return m_rim_color; -} - -float KRModel::getRimPower() -{ - return m_rim_power; -} - -void KRModel::setLightMap(const std::string &name) -{ - m_lightMap = name; - m_pLightMap = NULL; -} - -std::string KRModel::getLightMap() -{ - return m_lightMap; -} - -void KRModel::loadModel() { - if(m_models.size() == 0) { - std::vector models = m_pContext->getMeshManager()->getModel(m_model_name.c_str()); // The model manager returns the LOD levels in sorted order, with the highest detail first - unordered_map > bones; - if(models.size() > 0) { - bool all_bones_found = true; - for(std::vector::iterator model_itr = models.begin(); model_itr != models.end(); model_itr++) { - KRMesh *model = *model_itr; - std::vector model_bones; - int bone_count = model->getBoneCount(); - for(int bone_index=0; bone_index < bone_count; bone_index++) { - KRBone *matching_bone = dynamic_cast(getScene().getRootNode()->find(model->getBoneName(bone_index))); - if(matching_bone) { - model_bones.push_back(matching_bone); - } else { - all_bones_found = false; // Reject when there are any missing bones or multiple matches - } - } - bones[model] = model_bones; - } - if(all_bones_found) { - m_models = models; - m_bones = bones; - getScene().notify_sceneGraphModify(this); - } - - invalidateBounds(); - } - } -} - -void KRModel::render(KRCamera *pCamera, std::vector &point_lights, std::vector &directional_lights, std::vector&spot_lights, const KRViewport &viewport, KRNode::RenderPass renderPass) { - - if(m_lod_visible >= LOD_VISIBILITY_PRESTREAM && renderPass == KRNode::RENDER_PASS_PRESTREAM) { - preStream(viewport); - } - - if(m_lod_visible <= LOD_VISIBILITY_PRESTREAM) return; - - KRNode::render(pCamera, point_lights, directional_lights, spot_lights, viewport, renderPass); - - if(renderPass != KRNode::RENDER_PASS_DEFERRED_LIGHTS && renderPass != KRNode::RENDER_PASS_ADDITIVE_PARTICLES && renderPass != KRNode::RENDER_PASS_PARTICLE_OCCLUSION && renderPass != KRNode::RENDER_PASS_VOLUMETRIC_EFFECTS_ADDITIVE && renderPass != KRNode::RENDER_PASS_GENERATE_SHADOWMAPS && renderPass != KRNode::RENDER_PASS_PRESTREAM) { - loadModel(); - - if(m_models.size() > 0) { - // Don't render meshes on second pass of the deferred lighting renderer, as only lights will be applied - - /* - float lod_coverage = 0.0f; - if(m_models.size() > 1) { - lod_coverage = viewport.coverage(getBounds()); // This also checks the view frustrum culling - } else if(viewport.visible(getBounds())) { - lod_coverage = 1.0f; - } - */ - - float lod_coverage = viewport.coverage(getBounds()); // This also checks the view frustrum culling - - if(lod_coverage > m_min_lod_coverage) { - - // ---===--- Select the best LOD model based on screen coverage ---===--- - std::vector::iterator itr=m_models.begin(); - KRMesh *pModel = *itr++; - - while(itr != m_models.end()) { - KRMesh *pLODModel = *itr++; - if((float)pLODModel->getLODCoverage() / 100.0f > lod_coverage && pLODModel->getLODCoverage() < pModel->getLODCoverage()) { - pModel = pLODModel; - } else { - break; - } - } - - if(m_pLightMap == NULL && m_lightMap.size()) { - m_pLightMap = getContext().getTextureManager()->getTexture(m_lightMap); - } - - if(m_pLightMap && pCamera->settings.bEnableLightMap && renderPass != RENDER_PASS_SHADOWMAP && renderPass != RENDER_PASS_GENERATE_SHADOWMAPS) { - m_pContext->getTextureManager()->selectTexture(5, m_pLightMap, lod_coverage, KRTexture::TEXTURE_USAGE_LIGHT_MAP); - } - - Matrix4 matModel = getModelMatrix(); - if(m_faces_camera) { - Vector3 model_center = Matrix4::Dot(matModel, Vector3::Zero()); - Vector3 camera_pos = viewport.getCameraPosition(); - matModel = Quaternion(Vector3::Forward(), Vector3::Normalize(camera_pos - model_center)).rotationMatrix() * matModel; - } - - pModel->render(getName(), pCamera, point_lights, directional_lights, spot_lights, viewport, matModel, m_pLightMap, renderPass, m_bones[pModel], m_rim_color, m_rim_power, lod_coverage); - } - } - } -} - -void KRModel::preStream(const KRViewport &viewport) -{ - loadModel(); - float lod_coverage = viewport.coverage(getBounds()); - - for(auto itr = m_models.begin(); itr != m_models.end(); itr++) { - (*itr)->preStream(lod_coverage); - } - - if(m_pLightMap == NULL && m_lightMap.size()) { - m_pLightMap = getContext().getTextureManager()->getTexture(m_lightMap); - } - - if(m_pLightMap) { - m_pLightMap->resetPoolExpiry(lod_coverage, KRTexture::TEXTURE_USAGE_LIGHT_MAP); - } -} - - -kraken_stream_level KRModel::getStreamLevel(const KRViewport &viewport) -{ - kraken_stream_level stream_level = KRNode::getStreamLevel(viewport); - - loadModel(); - - for(auto itr = m_models.begin(); itr != m_models.end(); itr++) { - stream_level = KRMIN(stream_level, (*itr)->getStreamLevel()); - } - - return stream_level; -} - -AABB KRModel::getBounds() { - loadModel(); - if(m_models.size() > 0) { - if(m_faces_camera) { - AABB normal_bounds = AABB(m_models[0]->getMinPoint(), m_models[0]->getMaxPoint(), getModelMatrix()); - float max_dimension = normal_bounds.longest_radius(); - return AABB(normal_bounds.center()-Vector3(max_dimension), normal_bounds.center() + Vector3(max_dimension)); - } else { - - if(!(m_boundsCachedMat == getModelMatrix())) { - m_boundsCachedMat = getModelMatrix(); - m_boundsCached = AABB(m_models[0]->getMinPoint(), m_models[0]->getMaxPoint(), getModelMatrix()); - } - return m_boundsCached; - } - } else { - return AABB::Infinite(); - } -} - +// +// KRModel.cpp +// KREngine +// +// Copyright 2012 Kearwood Gilbert. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other materials +// provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// The views and conclusions contained in the software and documentation are those of the +// authors and should not be interpreted as representing official policies, either expressed +// or implied, of Kearwood Gilbert. +// + +#include "KREngine-common.h" + +#include "KRModel.h" +#include "KRContext.h" +#include "KRMesh.h" + +KRModel::KRModel(KRScene &scene, std::string instance_name, std::string model_name, std::string light_map, float lod_min_coverage, bool receives_shadow, bool faces_camera, Vector3 rim_color, float rim_power) : KRNode(scene, instance_name) { + m_lightMap = light_map; + m_pLightMap = NULL; + m_model_name = model_name; + m_min_lod_coverage = lod_min_coverage; + m_receivesShadow = receives_shadow; + m_faces_camera = faces_camera; + m_rim_color = rim_color; + m_rim_power = rim_power; + + m_boundsCachedMat.c[0] = -1.0f; + m_boundsCachedMat.c[1] = -1.0f; + m_boundsCachedMat.c[2] = -1.0f; + m_boundsCachedMat.c[3] = -1.0f; + m_boundsCachedMat.c[4] = -1.0f; + m_boundsCachedMat.c[5] = -1.0f; + m_boundsCachedMat.c[6] = -1.0f; + m_boundsCachedMat.c[7] = -1.0f; + m_boundsCachedMat.c[8] = -1.0f; + m_boundsCachedMat.c[9] = -1.0f; + m_boundsCachedMat.c[10] = -1.0f; + m_boundsCachedMat.c[11] = -1.0f; + m_boundsCachedMat.c[12] = -1.0f; + m_boundsCachedMat.c[13] = -1.0f; + m_boundsCachedMat.c[14] = -1.0f; + m_boundsCachedMat.c[15] = -1.0f; +} + +KRModel::~KRModel() { + +} + +std::string KRModel::getElementName() { + return "model"; +} + +tinyxml2::XMLElement *KRModel::saveXML( tinyxml2::XMLNode *parent) +{ + tinyxml2::XMLElement *e = KRNode::saveXML(parent); + e->SetAttribute("mesh", m_model_name.c_str()); + e->SetAttribute("light_map", m_lightMap.c_str()); + e->SetAttribute("lod_min_coverage", m_min_lod_coverage); + e->SetAttribute("receives_shadow", m_receivesShadow ? "true" : "false"); + e->SetAttribute("faces_camera", m_faces_camera ? "true" : "false"); + kraken::setXMLAttribute("rim_color", e, m_rim_color, Vector3::Zero()); + e->SetAttribute("rim_power", m_rim_power); + return e; +} + +void KRModel::setRimColor(const Vector3 &rim_color) +{ + m_rim_color = rim_color; +} + +void KRModel::setRimPower(float rim_power) +{ + m_rim_power = rim_power; +} + +Vector3 KRModel::getRimColor() +{ + return m_rim_color; +} + +float KRModel::getRimPower() +{ + return m_rim_power; +} + +void KRModel::setLightMap(const std::string &name) +{ + m_lightMap = name; + m_pLightMap = NULL; +} + +std::string KRModel::getLightMap() +{ + return m_lightMap; +} + +void KRModel::loadModel() { + if(m_models.size() == 0) { + std::vector models = m_pContext->getMeshManager()->getModel(m_model_name.c_str()); // The model manager returns the LOD levels in sorted order, with the highest detail first + unordered_map > bones; + if(models.size() > 0) { + bool all_bones_found = true; + for(std::vector::iterator model_itr = models.begin(); model_itr != models.end(); model_itr++) { + KRMesh *model = *model_itr; + std::vector model_bones; + int bone_count = model->getBoneCount(); + for(int bone_index=0; bone_index < bone_count; bone_index++) { + KRBone *matching_bone = dynamic_cast(getScene().getRootNode()->find(model->getBoneName(bone_index))); + if(matching_bone) { + model_bones.push_back(matching_bone); + } else { + all_bones_found = false; // Reject when there are any missing bones or multiple matches + } + } + bones[model] = model_bones; + } + if(all_bones_found) { + m_models = models; + m_bones = bones; + getScene().notify_sceneGraphModify(this); + } + + invalidateBounds(); + } + } +} + +void KRModel::render(KRCamera *pCamera, std::vector &point_lights, std::vector &directional_lights, std::vector&spot_lights, const KRViewport &viewport, KRNode::RenderPass renderPass) { + + if(m_lod_visible >= LOD_VISIBILITY_PRESTREAM && renderPass == KRNode::RENDER_PASS_PRESTREAM) { + preStream(viewport); + } + + if(m_lod_visible <= LOD_VISIBILITY_PRESTREAM) return; + + KRNode::render(pCamera, point_lights, directional_lights, spot_lights, viewport, renderPass); + + if(renderPass != KRNode::RENDER_PASS_DEFERRED_LIGHTS && renderPass != KRNode::RENDER_PASS_ADDITIVE_PARTICLES && renderPass != KRNode::RENDER_PASS_PARTICLE_OCCLUSION && renderPass != KRNode::RENDER_PASS_VOLUMETRIC_EFFECTS_ADDITIVE && renderPass != KRNode::RENDER_PASS_GENERATE_SHADOWMAPS && renderPass != KRNode::RENDER_PASS_PRESTREAM) { + loadModel(); + + if(m_models.size() > 0) { + // Don't render meshes on second pass of the deferred lighting renderer, as only lights will be applied + + /* + float lod_coverage = 0.0f; + if(m_models.size() > 1) { + lod_coverage = viewport.coverage(getBounds()); // This also checks the view frustrum culling + } else if(viewport.visible(getBounds())) { + lod_coverage = 1.0f; + } + */ + + float lod_coverage = viewport.coverage(getBounds()); // This also checks the view frustrum culling + + if(lod_coverage > m_min_lod_coverage) { + + // ---===--- Select the best LOD model based on screen coverage ---===--- + std::vector::iterator itr=m_models.begin(); + KRMesh *pModel = *itr++; + + while(itr != m_models.end()) { + KRMesh *pLODModel = *itr++; + if((float)pLODModel->getLODCoverage() / 100.0f > lod_coverage && pLODModel->getLODCoverage() < pModel->getLODCoverage()) { + pModel = pLODModel; + } else { + break; + } + } + + if(m_pLightMap == NULL && m_lightMap.size()) { + m_pLightMap = getContext().getTextureManager()->getTexture(m_lightMap); + } + + if(m_pLightMap && pCamera->settings.bEnableLightMap && renderPass != RENDER_PASS_SHADOWMAP && renderPass != RENDER_PASS_GENERATE_SHADOWMAPS) { + m_pContext->getTextureManager()->selectTexture(5, m_pLightMap, lod_coverage, KRTexture::TEXTURE_USAGE_LIGHT_MAP); + } + + Matrix4 matModel = getModelMatrix(); + if(m_faces_camera) { + Vector3 model_center = Matrix4::Dot(matModel, Vector3::Zero()); + Vector3 camera_pos = viewport.getCameraPosition(); + matModel = Quaternion::Create(Vector3::Forward(), Vector3::Normalize(camera_pos - model_center)).rotationMatrix() * matModel; + } + + pModel->render(getName(), pCamera, point_lights, directional_lights, spot_lights, viewport, matModel, m_pLightMap, renderPass, m_bones[pModel], m_rim_color, m_rim_power, lod_coverage); + } + } + } +} + +void KRModel::preStream(const KRViewport &viewport) +{ + loadModel(); + float lod_coverage = viewport.coverage(getBounds()); + + for(auto itr = m_models.begin(); itr != m_models.end(); itr++) { + (*itr)->preStream(lod_coverage); + } + + if(m_pLightMap == NULL && m_lightMap.size()) { + m_pLightMap = getContext().getTextureManager()->getTexture(m_lightMap); + } + + if(m_pLightMap) { + m_pLightMap->resetPoolExpiry(lod_coverage, KRTexture::TEXTURE_USAGE_LIGHT_MAP); + } +} + + +kraken_stream_level KRModel::getStreamLevel(const KRViewport &viewport) +{ + kraken_stream_level stream_level = KRNode::getStreamLevel(viewport); + + loadModel(); + + for(auto itr = m_models.begin(); itr != m_models.end(); itr++) { + stream_level = KRMIN(stream_level, (*itr)->getStreamLevel()); + } + + return stream_level; +} + +AABB KRModel::getBounds() { + loadModel(); + if(m_models.size() > 0) { + if(m_faces_camera) { + AABB normal_bounds = AABB::Create(m_models[0]->getMinPoint(), m_models[0]->getMaxPoint(), getModelMatrix()); + float max_dimension = normal_bounds.longest_radius(); + return AABB::Create(normal_bounds.center()-Vector3::Create(max_dimension), normal_bounds.center() + Vector3::Create(max_dimension)); + } else { + + if(!(m_boundsCachedMat == getModelMatrix())) { + m_boundsCachedMat = getModelMatrix(); + m_boundsCached = AABB::Create(m_models[0]->getMinPoint(), m_models[0]->getMaxPoint(), getModelMatrix()); + } + return m_boundsCached; + } + } else { + return AABB::Infinite(); + } +} + diff --git a/kraken/KRNode.cpp b/kraken/KRNode.cpp index 6a5fe1a..aa5386a 100755 --- a/kraken/KRNode.cpp +++ b/kraken/KRNode.cpp @@ -1,963 +1,963 @@ -// -// KRNode.cpp -// KREngine -// -// Created by Kearwood Gilbert on 12-04-11. -// Copyright (c) 2012 Kearwood Software. All rights reserved. -// - -#include "KREngine-common.h" - -#include "KRNode.h" -#include "KRLODGroup.h" -#include "KRLODSet.h" -#include "KRPointLight.h" -#include "KRSpotLight.h" -#include "KRDirectionalLight.h" -#include "KRModel.h" -#include "KRCollider.h" -#include "KRParticleSystem.h" -#include "KRParticleSystemNewtonian.h" -#include "KRBone.h" -#include "KRLocator.h" -#include "KRAudioSource.h" -#include "KRAmbientZone.h" -#include "KRReverbZone.h" -#include "KRSprite.h" - -KRNode::KRNode(KRScene &scene, std::string name) : KRContextObject(scene.getContext()) -{ - m_name = name; - m_localScale = Vector3::One(); - m_localRotation = Vector3::Zero(); - m_localTranslation = Vector3::Zero(); - m_initialLocalTranslation = m_localTranslation; - m_initialLocalScale = m_localScale; - m_initialLocalRotation = m_localRotation; - - - - m_rotationOffset = Vector3::Zero(); - m_scalingOffset = Vector3::Zero(); - m_rotationPivot = Vector3::Zero(); - m_scalingPivot = Vector3::Zero(); - m_preRotation = Vector3::Zero(); - m_postRotation = Vector3::Zero(); - - m_initialRotationOffset = Vector3::Zero(); - m_initialScalingOffset = Vector3::Zero(); - m_initialRotationPivot = Vector3::Zero(); - m_initialScalingPivot = Vector3::Zero(); - m_initialPreRotation = Vector3::Zero(); - m_initialPostRotation = Vector3::Zero(); - - m_parentNode = NULL; - m_pScene = &scene; - m_modelMatrixValid = false; - m_inverseModelMatrixValid = false; - m_bindPoseMatrixValid = false; - m_activePoseMatrixValid = false; - m_inverseBindPoseMatrixValid = false; - m_modelMatrix = Matrix4(); - m_bindPoseMatrix = Matrix4(); - m_activePoseMatrix = Matrix4(); - m_lod_visible = LOD_VISIBILITY_HIDDEN; - m_scale_compensation = false; - m_boundsValid = false; - - m_lastRenderFrame = -1000; - for(int i=0; i < KRENGINE_NODE_ATTRIBUTE_COUNT; i++) { - m_animation_mask[i] = false; - } -} - -KRNode::~KRNode() { - - - while(m_childNodes.size() > 0) { - delete *m_childNodes.begin(); - } - - for(std::set::iterator itr = m_behaviors.begin(); itr != m_behaviors.end(); itr++) { - delete *itr; - } - m_behaviors.clear(); - - if(m_parentNode) { - m_parentNode->childDeleted(this); - } - - getScene().notify_sceneGraphDelete(this); - -} - -void KRNode::setScaleCompensation(bool scale_compensation) -{ - if(m_scale_compensation != scale_compensation) { - m_scale_compensation = scale_compensation; - invalidateModelMatrix(); - invalidateBindPoseMatrix(); - } -} -bool KRNode::getScaleCompensation() -{ - return m_scale_compensation; -} - -void KRNode::childDeleted(KRNode *child_node) -{ - m_childNodes.erase(child_node); - invalidateBounds(); - getScene().notify_sceneGraphModify(this); -} - -void KRNode::addChild(KRNode *child) { - assert(child->m_parentNode == NULL); - child->m_parentNode = this; - m_childNodes.insert(child); - child->setLODVisibility(m_lod_visible); // Child node inherits LOD visibility status from parent -} - -tinyxml2::XMLElement *KRNode::saveXML(tinyxml2::XMLNode *parent) { - tinyxml2::XMLDocument *doc = parent->GetDocument(); - tinyxml2::XMLElement *e = doc->NewElement(getElementName().c_str()); - tinyxml2::XMLNode *n = parent->InsertEndChild(e); - e->SetAttribute("name", m_name.c_str()); - kraken::setXMLAttribute("translate", e, m_localTranslation, Vector3::Zero()); - kraken::setXMLAttribute("scale", e, m_localScale, Vector3::One()); - kraken::setXMLAttribute("rotate", e, (m_localRotation * (180.0f / M_PI)), Vector3::Zero()); - kraken::setXMLAttribute("rotate_offset", e, m_rotationOffset, Vector3::Zero()); - kraken::setXMLAttribute("scale_offset", e, m_scalingOffset, Vector3::Zero()); - kraken::setXMLAttribute("rotate_pivot", e, m_rotationPivot, Vector3::Zero()); - kraken::setXMLAttribute("scale_pivot", e, m_scalingPivot, Vector3::Zero()); - kraken::setXMLAttribute("pre_rotate", e, (m_preRotation * (180.0f / M_PI)), Vector3::Zero()); - kraken::setXMLAttribute("post_rotate", e, (m_postRotation * (180.0f / M_PI)), Vector3::Zero()); - - for(std::set::iterator itr=m_childNodes.begin(); itr != m_childNodes.end(); ++itr) { - KRNode *child = (*itr); - child->saveXML(n); - } - return e; -} - -void KRNode::loadXML(tinyxml2::XMLElement *e) { - m_name = e->Attribute("name"); - m_localTranslation = kraken::getXMLAttribute("translate", e, Vector3::Zero()); - m_localScale = kraken::getXMLAttribute("scale", e, Vector3::One()); - m_localRotation = kraken::getXMLAttribute("rotate", e, Vector3::Zero()); - m_localRotation *= M_PI / 180.0f; // Convert degrees to radians - m_preRotation = kraken::getXMLAttribute("pre_rotate", e, Vector3::Zero()); - m_preRotation *= M_PI / 180.0f; // Convert degrees to radians - m_postRotation = kraken::getXMLAttribute("post_rotate", e, Vector3::Zero()); - m_postRotation *= M_PI / 180.0f; // Convert degrees to radians - - m_rotationOffset = kraken::getXMLAttribute("rotate_offset", e, Vector3::Zero()); - m_scalingOffset = kraken::getXMLAttribute("scale_offset", e, Vector3::Zero()); - m_rotationPivot = kraken::getXMLAttribute("rotate_pivot", e, Vector3::Zero()); - m_scalingPivot = kraken::getXMLAttribute("scale_pivot", e, Vector3::Zero()); - - m_initialLocalTranslation = m_localTranslation; - m_initialLocalScale = m_localScale; - m_initialLocalRotation = m_localRotation; - - m_initialRotationOffset = m_rotationOffset; - m_initialScalingOffset = m_scalingOffset; - m_initialRotationPivot = m_rotationPivot; - m_initialScalingPivot = m_scalingPivot; - m_initialPreRotation = m_preRotation; - m_initialPostRotation = m_postRotation; - - m_bindPoseMatrixValid = false; - m_activePoseMatrixValid = false; - m_inverseBindPoseMatrixValid = false; - m_modelMatrixValid = false; - m_inverseModelMatrixValid = false; - - for(tinyxml2::XMLElement *child_element=e->FirstChildElement(); child_element != NULL; child_element = child_element->NextSiblingElement()) { - const char *szElementName = child_element->Name(); - if(strcmp(szElementName, "behavior") == 0) { - KRBehavior *behavior = KRBehavior::LoadXML(this, child_element); - if(behavior) { - addBehavior(behavior); - behavior->init(); - } - } else { - KRNode *child_node = KRNode::LoadXML(getScene(), child_element); - - if(child_node) { - addChild(child_node); - } - } - } -} - -void KRNode::setLocalTranslation(const Vector3 &v, bool set_original) { - m_localTranslation = v; - if(set_original) { - m_initialLocalTranslation = v; - invalidateBindPoseMatrix(); - } - invalidateModelMatrix(); -} - -void KRNode::setWorldTranslation(const Vector3 &v) -{ - if(m_parentNode) { - setLocalTranslation(Matrix4::Dot(m_parentNode->getInverseModelMatrix(), v)); - } else { - setLocalTranslation(v); - } -} - - -void KRNode::setWorldRotation(const Vector3 &v) -{ - if(m_parentNode) { - setLocalRotation((Quaternion(v) * -m_parentNode->getWorldRotation()).eulerXYZ()); - setPreRotation(Vector3::Zero()); - setPostRotation(Vector3::Zero()); - } else { - setLocalRotation(v); - setPreRotation(Vector3::Zero()); - setPostRotation(Vector3::Zero()); - } -} - - -void KRNode::setWorldScale(const Vector3 &v) -{ - if(m_parentNode) { - setLocalScale(Matrix4::DotNoTranslate(m_parentNode->getInverseModelMatrix(), v)); - } else { - setLocalScale(v); - } -} - -void KRNode::setLocalScale(const Vector3 &v, bool set_original) { - m_localScale = v; - if(set_original) { - m_initialLocalScale = v; - invalidateBindPoseMatrix(); - } - invalidateModelMatrix(); -} - -void KRNode::setLocalRotation(const Vector3 &v, bool set_original) { - m_localRotation = v; - if(set_original) { - m_initialLocalRotation = v; - invalidateBindPoseMatrix(); - } - invalidateModelMatrix(); -} - - -void KRNode::setRotationOffset(const Vector3 &v, bool set_original) -{ - m_rotationOffset = v; - if(set_original) { - m_initialRotationOffset = v; - invalidateBindPoseMatrix(); - } - invalidateModelMatrix(); -} - -void KRNode::setScalingOffset(const Vector3 &v, bool set_original) -{ - m_scalingOffset = v; - if(set_original) { - m_initialScalingOffset = v; - invalidateBindPoseMatrix(); - } - invalidateModelMatrix(); -} - -void KRNode::setRotationPivot(const Vector3 &v, bool set_original) -{ - m_rotationPivot = v; - if(set_original) { - m_initialRotationPivot = v; - invalidateBindPoseMatrix(); - } - invalidateModelMatrix(); -} -void KRNode::setScalingPivot(const Vector3 &v, bool set_original) -{ - m_scalingPivot = v; - if(set_original) { - m_initialScalingPivot = v; - invalidateBindPoseMatrix(); - } - invalidateModelMatrix(); -} -void KRNode::setPreRotation(const Vector3 &v, bool set_original) -{ - m_preRotation = v; - if(set_original) { - m_initialPreRotation = v; - invalidateBindPoseMatrix(); - } - invalidateModelMatrix(); -} -void KRNode::setPostRotation(const Vector3 &v, bool set_original) -{ - m_postRotation = v; - if(set_original) { - m_initialPostRotation = v; - invalidateBindPoseMatrix(); - } - invalidateModelMatrix(); -} - -const Vector3 &KRNode::getRotationOffset() -{ - return m_rotationOffset; -} -const Vector3 &KRNode::getScalingOffset() -{ - return m_scalingOffset; -} -const Vector3 &KRNode::getRotationPivot() -{ - return m_rotationPivot; -} -const Vector3 &KRNode::getScalingPivot() -{ - return m_scalingPivot; -} -const Vector3 &KRNode::getPreRotation() -{ - return m_preRotation; -} -const Vector3 &KRNode::getPostRotation() -{ - return m_postRotation; -} -const Vector3 &KRNode::getInitialRotationOffset() -{ - return m_initialRotationOffset; -} -const Vector3 &KRNode::getInitialScalingOffset() -{ - return m_initialScalingOffset; -} -const Vector3 &KRNode::getInitialRotationPivot() -{ - return m_initialRotationPivot; -} -const Vector3 &KRNode::getInitialScalingPivot() -{ - return m_initialScalingPivot; -} -const Vector3 &KRNode::getInitialPreRotation() -{ - return m_initialPreRotation; -} -const Vector3 &KRNode::getInitialPostRotation() -{ - return m_initialPostRotation; -} - -const Vector3 &KRNode::getLocalTranslation() { - return m_localTranslation; -} -const Vector3 &KRNode::getLocalScale() { - return m_localScale; -} -const Vector3 &KRNode::getLocalRotation() { - return m_localRotation; -} - -const Vector3 &KRNode::getInitialLocalTranslation() { - return m_initialLocalTranslation; -} -const Vector3 &KRNode::getInitialLocalScale() { - return m_initialLocalScale; -} -const Vector3 &KRNode::getInitialLocalRotation() { - return m_initialLocalRotation; -} - -const Vector3 KRNode::getWorldTranslation() { - return localToWorld(Vector3::Zero()); -} - -const Vector3 KRNode::getWorldScale() { - return Matrix4::DotNoTranslate(getModelMatrix(), m_localScale); -} - -std::string KRNode::getElementName() { - return "node"; -} - -KRNode *KRNode::LoadXML(KRScene &scene, tinyxml2::XMLElement *e) { - KRNode *new_node = NULL; - const char *szElementName = e->Name(); - const char *szName = e->Attribute("name"); - if(strcmp(szElementName, "node") == 0) { - new_node = new KRNode(scene, szName); - } else if(strcmp(szElementName, "lod_set") == 0) { - new_node = new KRLODSet(scene, szName); - } else if(strcmp(szElementName, "lod_group") == 0) { - new_node = new KRLODGroup(scene, szName); - } else if(strcmp(szElementName, "point_light") == 0) { - new_node = new KRPointLight(scene, szName); - } else if(strcmp(szElementName, "directional_light") == 0) { - new_node = new KRDirectionalLight(scene, szName); - } else if(strcmp(szElementName, "spot_light") == 0) { - new_node = new KRSpotLight(scene, szName); - } else if(strcmp(szElementName, "particles_newtonian") == 0) { - new_node = new KRParticleSystemNewtonian(scene, szName); - } else if(strcmp(szElementName, "sprite") == 0) { - new_node = new KRSprite(scene, szName); - } else if(strcmp(szElementName, "model") == 0) { - float lod_min_coverage = 0.0f; - if(e->QueryFloatAttribute("lod_min_coverage", &lod_min_coverage) != tinyxml2::XML_SUCCESS) { - lod_min_coverage = 0.0f; - } - bool receives_shadow = true; - if(e->QueryBoolAttribute("receives_shadow", &receives_shadow) != tinyxml2::XML_SUCCESS) { - receives_shadow = true; - } - bool faces_camera = false; - if(e->QueryBoolAttribute("faces_camera", &faces_camera) != tinyxml2::XML_SUCCESS) { - faces_camera = false; - } - float rim_power = 0.0f; - if(e->QueryFloatAttribute("rim_power", &rim_power) != tinyxml2::XML_SUCCESS) { - rim_power = 0.0f; - } - Vector3 rim_color = Vector3::Zero(); - rim_color = kraken::getXMLAttribute("rim_color", e, Vector3::Zero()); - new_node = new KRModel(scene, szName, e->Attribute("mesh"), e->Attribute("light_map"), lod_min_coverage, receives_shadow, faces_camera, rim_color, rim_power); - } else if(strcmp(szElementName, "collider") == 0) { - new_node = new KRCollider(scene, szName, e->Attribute("mesh"), 65535, 1.0f); - } else if(strcmp(szElementName, "bone") == 0) { - new_node = new KRBone(scene, szName); - } else if(strcmp(szElementName, "locator") == 0) { - new_node = new KRLocator(scene, szName); - } else if(strcmp(szElementName, "audio_source") == 0) { - new_node = new KRAudioSource(scene, szName); - } else if(strcmp(szElementName, "ambient_zone") == 0) { - new_node = new KRAmbientZone(scene, szName); - } else if(strcmp(szElementName, "reverb_zone") == 0) { - new_node = new KRReverbZone(scene, szName); - } else if(strcmp(szElementName, "camera") == 0) { - new_node = new KRCamera(scene, szName); - } - - if(new_node) { - new_node->loadXML(e); - } - - return new_node; -} - -void KRNode::render(KRCamera *pCamera, std::vector &point_lights, std::vector &directional_lights, std::vector&spot_lights, const KRViewport &viewport, RenderPass renderPass) -{ - if(m_lod_visible <= LOD_VISIBILITY_PRESTREAM) return; - - m_lastRenderFrame = getContext().getCurrentFrame(); -} - -const std::set &KRNode::getChildren() { - return m_childNodes; -} - -KRNode *KRNode::getParent() { - return m_parentNode; -} - -const std::string &KRNode::getName() const { - return m_name; -} - -KRScene &KRNode::getScene() { - return *m_pScene; -} - -AABB KRNode::getBounds() { - if(!m_boundsValid) { - AABB bounds = AABB::Zero(); - - bool first_child = true; - for(std::set::iterator itr=m_childNodes.begin(); itr != m_childNodes.end(); ++itr) { - KRNode *child = (*itr); - if(child->getBounds() != AABB::Zero()) { - if(first_child) { - first_child = false; - bounds = child->getBounds(); - } else { - bounds.encapsulate(child->getBounds()); - } - } - } - - m_bounds = bounds; - m_boundsValid = true; - } - return m_bounds; -} - -void KRNode::invalidateModelMatrix() -{ - m_modelMatrixValid = false; - m_activePoseMatrixValid = false; - m_inverseModelMatrixValid = false; - for(std::set::iterator itr=m_childNodes.begin(); itr != m_childNodes.end(); ++itr) { - KRNode *child = (*itr); - child->invalidateModelMatrix(); - } - - invalidateBounds(); - getScene().notify_sceneGraphModify(this); -} - -void KRNode::invalidateBindPoseMatrix() -{ - m_bindPoseMatrixValid = false; - m_inverseBindPoseMatrixValid = false; - for(std::set::iterator itr=m_childNodes.begin(); itr != m_childNodes.end(); ++itr) { - KRNode *child = (*itr); - child->invalidateBindPoseMatrix(); - } -} - -const Matrix4 &KRNode::getModelMatrix() -{ - - if(!m_modelMatrixValid) { - m_modelMatrix = Matrix4(); - - bool parent_is_bone = false; - if(dynamic_cast(m_parentNode)) { - parent_is_bone = true; - } - - if(getScaleCompensation() && parent_is_bone) { - - - // WorldTransform = ParentWorldTransform * T * Roff * Rp * Rpre * R * Rpost * Rp-1 * Soff * Sp * S * Sp-1 - m_modelMatrix = Matrix4::Translation(-m_scalingPivot) - * Matrix4::Scaling(m_localScale) - * Matrix4::Translation(m_scalingPivot) - * Matrix4::Translation(m_scalingOffset) - * Matrix4::Translation(-m_rotationPivot) - //* (Quaternion(m_postRotation) * Quaternion(m_localRotation) * Quaternion(m_preRotation)).rotationMatrix() - * Matrix4::Rotation(m_postRotation) - * Matrix4::Rotation(m_localRotation) - * Matrix4::Rotation(m_preRotation) - * Matrix4::Translation(m_rotationPivot) - * Matrix4::Translation(m_rotationOffset); - - if(m_parentNode) { - - m_modelMatrix.rotate(m_parentNode->getWorldRotation()); - m_modelMatrix.translate(Matrix4::Dot(m_parentNode->getModelMatrix(), m_localTranslation)); - } else { - m_modelMatrix.translate(m_localTranslation); - } - } else { - - // WorldTransform = ParentWorldTransform * T * Roff * Rp * Rpre * R * Rpost * Rp-1 * Soff * Sp * S * Sp-1 - m_modelMatrix = Matrix4::Translation(-m_scalingPivot) - * Matrix4::Scaling(m_localScale) - * Matrix4::Translation(m_scalingPivot) - * Matrix4::Translation(m_scalingOffset) - * Matrix4::Translation(-m_rotationPivot) - //* (Quaternion(m_postRotation) * Quaternion(m_localRotation) * Quaternion(m_preRotation)).rotationMatrix() - * Matrix4::Rotation(m_postRotation) - * Matrix4::Rotation(m_localRotation) - * Matrix4::Rotation(m_preRotation) - * Matrix4::Translation(m_rotationPivot) - * Matrix4::Translation(m_rotationOffset) - * Matrix4::Translation(m_localTranslation); - - if(m_parentNode) { - m_modelMatrix *= m_parentNode->getModelMatrix(); - } - } - - m_modelMatrixValid = true; - - } - return m_modelMatrix; -} - -const Matrix4 &KRNode::getBindPoseMatrix() -{ - if(!m_bindPoseMatrixValid) { - m_bindPoseMatrix = Matrix4(); - - bool parent_is_bone = false; - if(dynamic_cast(m_parentNode)) { - parent_is_bone = true; - } - - if(getScaleCompensation() && parent_is_bone) { - m_bindPoseMatrix = Matrix4::Translation(-m_initialScalingPivot) - * Matrix4::Scaling(m_initialLocalScale) - * Matrix4::Translation(m_initialScalingPivot) - * Matrix4::Translation(m_initialScalingOffset) - * Matrix4::Translation(-m_initialRotationPivot) - //* (Quaternion(m_initialPostRotation) * Quaternion(m_initialLocalRotation) * Quaternion(m_initialPreRotation)).rotationMatrix() - * Matrix4::Rotation(m_initialPostRotation) - * Matrix4::Rotation(m_initialLocalRotation) - * Matrix4::Rotation(m_initialPreRotation) - * Matrix4::Translation(m_initialRotationPivot) - * Matrix4::Translation(m_initialRotationOffset); - //m_bindPoseMatrix.translate(m_localTranslation); - if(m_parentNode) { - - m_bindPoseMatrix.rotate(m_parentNode->getBindPoseWorldRotation()); - m_bindPoseMatrix.translate(Matrix4::Dot(m_parentNode->getBindPoseMatrix(), m_localTranslation)); - } else { - m_bindPoseMatrix.translate(m_localTranslation); - } - } else { - - // WorldTransform = ParentWorldTransform * T * Roff * Rp * Rpre * R * Rpost * Rp-1 * Soff * Sp * S * Sp-1 - - m_bindPoseMatrix = Matrix4::Translation(-m_initialScalingPivot) - * Matrix4::Scaling(m_initialLocalScale) - * Matrix4::Translation(m_initialScalingPivot) - * Matrix4::Translation(m_initialScalingOffset) - * Matrix4::Translation(-m_initialRotationPivot) - // * (Quaternion(m_initialPostRotation) * Quaternion(m_initialLocalRotation) * Quaternion(m_initialPreRotation)).rotationMatrix() - * Matrix4::Rotation(m_initialPostRotation) - * Matrix4::Rotation(m_initialLocalRotation) - * Matrix4::Rotation(m_initialPreRotation) - * Matrix4::Translation(m_initialRotationPivot) - * Matrix4::Translation(m_initialRotationOffset) - * Matrix4::Translation(m_initialLocalTranslation); - - if(m_parentNode && parent_is_bone) { - - m_bindPoseMatrix *= m_parentNode->getBindPoseMatrix(); - } - } - - m_bindPoseMatrixValid = true; - - } - return m_bindPoseMatrix; -} - -const Matrix4 &KRNode::getActivePoseMatrix() -{ - - if(!m_activePoseMatrixValid) { - m_activePoseMatrix = Matrix4(); - - bool parent_is_bone = false; - if(dynamic_cast(m_parentNode)) { - parent_is_bone = true; - } - - if(getScaleCompensation() && parent_is_bone) { - m_activePoseMatrix= Matrix4::Translation(-m_scalingPivot) - * Matrix4::Scaling(m_localScale) - * Matrix4::Translation(m_scalingPivot) - * Matrix4::Translation(m_scalingOffset) - * Matrix4::Translation(-m_rotationPivot) - * Matrix4::Rotation(m_postRotation) - * Matrix4::Rotation(m_localRotation) - * Matrix4::Rotation(m_preRotation) - * Matrix4::Translation(m_rotationPivot) - * Matrix4::Translation(m_rotationOffset); - - if(m_parentNode) { - - m_activePoseMatrix.rotate(m_parentNode->getActivePoseWorldRotation()); - m_activePoseMatrix.translate(Matrix4::Dot(m_parentNode->getActivePoseMatrix(), m_localTranslation)); - } else { - m_activePoseMatrix.translate(m_localTranslation); - } - } else { - - // WorldTransform = ParentWorldTransform * T * Roff * Rp * Rpre * R * Rpost * Rp-1 * Soff * Sp * S * Sp-1 - m_activePoseMatrix = Matrix4::Translation(-m_scalingPivot) - * Matrix4::Scaling(m_localScale) - * Matrix4::Translation(m_scalingPivot) - * Matrix4::Translation(m_scalingOffset) - * Matrix4::Translation(-m_rotationPivot) - * Matrix4::Rotation(m_postRotation) - * Matrix4::Rotation(m_localRotation) - * Matrix4::Rotation(m_preRotation) - * Matrix4::Translation(m_rotationPivot) - * Matrix4::Translation(m_rotationOffset) - * Matrix4::Translation(m_localTranslation); - - - if(m_parentNode && parent_is_bone) { - m_activePoseMatrix *= m_parentNode->getActivePoseMatrix(); - } - } - - m_activePoseMatrixValid = true; - - } - return m_activePoseMatrix; - -} - -const Quaternion KRNode::getWorldRotation() { - Quaternion world_rotation = Quaternion(m_postRotation) * Quaternion(m_localRotation) * Quaternion(m_preRotation); - if(m_parentNode) { - world_rotation = world_rotation * m_parentNode->getWorldRotation(); - } - return world_rotation; -} - -const Quaternion KRNode::getBindPoseWorldRotation() { - Quaternion world_rotation = Quaternion(m_initialPostRotation) * Quaternion(m_initialLocalRotation) * Quaternion(m_initialPreRotation); - if(dynamic_cast(m_parentNode)) { - world_rotation = world_rotation * m_parentNode->getBindPoseWorldRotation(); - } - return world_rotation; -} - -const Quaternion KRNode::getActivePoseWorldRotation() { - Quaternion world_rotation = Quaternion(m_postRotation) * Quaternion(m_localRotation) * Quaternion(m_preRotation); - if(dynamic_cast(m_parentNode)) { - world_rotation = world_rotation * m_parentNode->getActivePoseWorldRotation(); - } - return world_rotation; -} - -const Matrix4 &KRNode::getInverseModelMatrix() -{ - if(!m_inverseModelMatrixValid) { - m_inverseModelMatrix = Matrix4::Invert(getModelMatrix()); - } - return m_inverseModelMatrix; -} - -const Matrix4 &KRNode::getInverseBindPoseMatrix() -{ - if(!m_inverseBindPoseMatrixValid ) { - m_inverseBindPoseMatrix = Matrix4::Invert(getBindPoseMatrix()); - m_inverseBindPoseMatrixValid = true; - } - return m_inverseBindPoseMatrix; -} - -void KRNode::physicsUpdate(float deltaTime) -{ - const long MIN_DISPLAY_FRAMES = 10; - bool visible = m_lastRenderFrame + MIN_DISPLAY_FRAMES >= getContext().getCurrentFrame(); - for(std::set::iterator itr=m_behaviors.begin(); itr != m_behaviors.end(); itr++) { - (*itr)->update(deltaTime); - if(visible) { - (*itr)->visibleUpdate(deltaTime); - } - } -} - -bool KRNode::hasPhysics() -{ - return m_behaviors.size() > 0; -} - -void KRNode::SetAttribute(node_attribute_type attrib, float v) -{ - if(m_animation_mask[attrib]) return; - - const float DEGREES_TO_RAD = M_PI / 180.0f; - - //printf("%s - ", m_name.c_str()); - switch(attrib) { - case KRENGINE_NODE_ATTRIBUTE_TRANSLATE_X: - setLocalTranslation(Vector3(v, m_localTranslation.y, m_localTranslation.z)); - break; - case KRENGINE_NODE_ATTRIBUTE_TRANSLATE_Y: - setLocalTranslation(Vector3(m_localTranslation.x, v, m_localTranslation.z)); - break; - case KRENGINE_NODE_ATTRIBUTE_TRANSLATE_Z: - setLocalTranslation(Vector3(m_localTranslation.x, m_localTranslation.y, v)); - break; - case KRENGINE_NODE_ATTRIBUTE_SCALE_X: - setLocalScale(Vector3(v, m_localScale.y, m_localScale.z)); - break; - case KRENGINE_NODE_ATTRIBUTE_SCALE_Y: - setLocalScale(Vector3(m_localScale.x, v, m_localScale.z)); - break; - case KRENGINE_NODE_ATTRIBUTE_SCALE_Z: - setLocalScale(Vector3(m_localScale.x, m_localScale.y, v)); - break; - case KRENGINE_NODE_ATTRIBUTE_ROTATE_X: - setLocalRotation(Vector3(v * DEGREES_TO_RAD, m_localRotation.y, m_localRotation.z)); - break; - case KRENGINE_NODE_ATTRIBUTE_ROTATE_Y: - setLocalRotation(Vector3(m_localRotation.x, v * DEGREES_TO_RAD, m_localRotation.z)); - break; - case KRENGINE_NODE_ATTRIBUTE_ROTATE_Z: - setLocalRotation(Vector3(m_localRotation.x, m_localRotation.y, v * DEGREES_TO_RAD)); - break; - - - case KRENGINE_NODE_ATTRIBUTE_PRE_ROTATION_X: - setPreRotation(Vector3(v * DEGREES_TO_RAD, m_preRotation.y, m_preRotation.z)); - break; - case KRENGINE_NODE_ATTRIBUTE_PRE_ROTATION_Y: - setPreRotation(Vector3(m_preRotation.x, v * DEGREES_TO_RAD, m_preRotation.z)); - break; - case KRENGINE_NODE_ATTRIBUTE_PRE_ROTATION_Z: - setPreRotation(Vector3(m_preRotation.x, m_preRotation.y, v * DEGREES_TO_RAD)); - break; - case KRENGINE_NODE_ATTRIBUTE_POST_ROTATION_X: - setPostRotation(Vector3(v * DEGREES_TO_RAD, m_postRotation.y, m_postRotation.z)); - break; - case KRENGINE_NODE_ATTRIBUTE_POST_ROTATION_Y: - setPostRotation(Vector3(m_postRotation.x, v * DEGREES_TO_RAD, m_postRotation.z)); - break; - case KRENGINE_NODE_ATTRIBUTE_POST_ROTATION_Z: - setPostRotation(Vector3(m_postRotation.x, m_postRotation.y, v * DEGREES_TO_RAD)); - break; - case KRENGINE_NODE_ATTRIBUTE_ROTATION_PIVOT_X: - setRotationPivot(Vector3(v, m_rotationPivot.y, m_rotationPivot.z)); - break; - case KRENGINE_NODE_ATTRIBUTE_ROTATION_PIVOT_Y: - setRotationPivot(Vector3(m_rotationPivot.x, v, m_rotationPivot.z)); - break; - case KRENGINE_NODE_ATTRIBUTE_ROTATION_PIVOT_Z: - setRotationPivot(Vector3(m_rotationPivot.x, m_rotationPivot.y, v)); - break; - case KRENGINE_NODE_ATTRIBUTE_SCALE_PIVOT_X: - setScalingPivot(Vector3(v, m_scalingPivot.y, m_scalingPivot.z)); - break; - case KRENGINE_NODE_ATTRIBUTE_SCALE_PIVOT_Y: - setScalingPivot(Vector3(m_scalingPivot.x, v, m_scalingPivot.z)); - break; - case KRENGINE_NODE_ATTRIBUTE_SCALE_PIVOT_Z: - setScalingPivot(Vector3(m_scalingPivot.x, m_scalingPivot.y, v)); - break; - case KRENGINE_NODE_ATTRIBUTE_ROTATE_OFFSET_X: - setRotationOffset(Vector3(v, m_rotationOffset.y, m_rotationOffset.z)); - break; - case KRENGINE_NODE_ATTRIBUTE_ROTATE_OFFSET_Y: - setRotationOffset(Vector3(m_rotationOffset.x, v, m_rotationOffset.z)); - break; - case KRENGINE_NODE_ATTRIBUTE_ROTATE_OFFSET_Z: - setRotationOffset(Vector3(m_rotationOffset.x, m_rotationOffset.y, v)); - break; - case KRENGINE_NODE_SCALE_OFFSET_X: - setScalingOffset(Vector3(v, m_scalingOffset.y, m_scalingOffset.z)); - break; - case KRENGINE_NODE_SCALE_OFFSET_Y: - setScalingOffset(Vector3(m_scalingOffset.x, v, m_scalingOffset.z)); - break; - case KRENGINE_NODE_SCALE_OFFSET_Z: - setScalingOffset(Vector3(m_scalingOffset.x, m_scalingOffset.y, v)); - break; - case KRENGINE_NODE_ATTRIBUTE_NONE: - case KRENGINE_NODE_ATTRIBUTE_COUNT: - // Suppress warnings - break; - } -} - -void KRNode::setAnimationEnabled(node_attribute_type attrib, bool enable) -{ - m_animation_mask[attrib] = !enable; -} -bool KRNode::getAnimationEnabled(node_attribute_type attrib) const -{ - return !m_animation_mask[attrib]; -} - -void KRNode::removeFromOctreeNodes() -{ - for(std::set::iterator itr=m_octree_nodes.begin(); itr != m_octree_nodes.end(); itr++) { - KROctreeNode *octree_node = *itr; - octree_node->remove(this); - - // FINDME, TODO - This should be moved to the KROctree class - while(octree_node) { - octree_node->trim(); - if(octree_node->isEmpty()) { - octree_node = octree_node->getParent(); - } else { - octree_node = NULL; - } - } - } - m_octree_nodes.clear(); -} - -void KRNode::addToOctreeNode(KROctreeNode *octree_node) -{ - m_octree_nodes.insert(octree_node); -} - -void KRNode::updateLODVisibility(const KRViewport &viewport) -{ - if(m_lod_visible >= LOD_VISIBILITY_PRESTREAM) { - for(std::set::iterator itr=m_childNodes.begin(); itr != m_childNodes.end(); ++itr) { - (*itr)->updateLODVisibility(viewport); - } - } -} - -void KRNode::setLODVisibility(KRNode::LodVisibility lod_visibility) -{ - if(m_lod_visible != lod_visibility) { - if(m_lod_visible == LOD_VISIBILITY_HIDDEN && lod_visibility >= LOD_VISIBILITY_PRESTREAM) { - getScene().notify_sceneGraphCreate(this); - } else if(m_lod_visible >= LOD_VISIBILITY_PRESTREAM && lod_visibility == LOD_VISIBILITY_HIDDEN) { - getScene().notify_sceneGraphDelete(this); - } - - m_lod_visible = lod_visibility; - - for(std::set::iterator itr=m_childNodes.begin(); itr != m_childNodes.end(); ++itr) { - (*itr)->setLODVisibility(lod_visibility); - } - } -} - -KRNode::LodVisibility KRNode::getLODVisibility() -{ - return m_lod_visible; -} - -const Vector3 KRNode::localToWorld(const Vector3 &local_point) -{ - return Matrix4::Dot(getModelMatrix(), local_point); -} - -const Vector3 KRNode::worldToLocal(const Vector3 &world_point) -{ - return Matrix4::Dot(getInverseModelMatrix(), world_point); -} - -void KRNode::addBehavior(KRBehavior *behavior) -{ - m_behaviors.insert(behavior); - behavior->__setNode(this); - getScene().notify_sceneGraphModify(this); -} - -std::set &KRNode::getBehaviors() -{ - return m_behaviors; -} - -kraken_stream_level KRNode::getStreamLevel(const KRViewport &viewport) -{ - kraken_stream_level stream_level = kraken_stream_level::STREAM_LEVEL_IN_HQ; - - for(std::set::iterator itr=m_childNodes.begin(); itr != m_childNodes.end(); ++itr) { - stream_level = KRMIN(stream_level, (*itr)->getStreamLevel(viewport)); - } - - return stream_level; -} - -void KRNode::invalidateBounds() const -{ - m_boundsValid = false; - if(m_parentNode) { - m_parentNode->invalidateBounds(); - } -} +// +// KRNode.cpp +// KREngine +// +// Created by Kearwood Gilbert on 12-04-11. +// Copyright (c) 2012 Kearwood Software. All rights reserved. +// + +#include "KREngine-common.h" + +#include "KRNode.h" +#include "KRLODGroup.h" +#include "KRLODSet.h" +#include "KRPointLight.h" +#include "KRSpotLight.h" +#include "KRDirectionalLight.h" +#include "KRModel.h" +#include "KRCollider.h" +#include "KRParticleSystem.h" +#include "KRParticleSystemNewtonian.h" +#include "KRBone.h" +#include "KRLocator.h" +#include "KRAudioSource.h" +#include "KRAmbientZone.h" +#include "KRReverbZone.h" +#include "KRSprite.h" + +KRNode::KRNode(KRScene &scene, std::string name) : KRContextObject(scene.getContext()) +{ + m_name = name; + m_localScale = Vector3::One(); + m_localRotation = Vector3::Zero(); + m_localTranslation = Vector3::Zero(); + m_initialLocalTranslation = m_localTranslation; + m_initialLocalScale = m_localScale; + m_initialLocalRotation = m_localRotation; + + + + m_rotationOffset = Vector3::Zero(); + m_scalingOffset = Vector3::Zero(); + m_rotationPivot = Vector3::Zero(); + m_scalingPivot = Vector3::Zero(); + m_preRotation = Vector3::Zero(); + m_postRotation = Vector3::Zero(); + + m_initialRotationOffset = Vector3::Zero(); + m_initialScalingOffset = Vector3::Zero(); + m_initialRotationPivot = Vector3::Zero(); + m_initialScalingPivot = Vector3::Zero(); + m_initialPreRotation = Vector3::Zero(); + m_initialPostRotation = Vector3::Zero(); + + m_parentNode = NULL; + m_pScene = &scene; + m_modelMatrixValid = false; + m_inverseModelMatrixValid = false; + m_bindPoseMatrixValid = false; + m_activePoseMatrixValid = false; + m_inverseBindPoseMatrixValid = false; + m_modelMatrix = Matrix4(); + m_bindPoseMatrix = Matrix4(); + m_activePoseMatrix = Matrix4(); + m_lod_visible = LOD_VISIBILITY_HIDDEN; + m_scale_compensation = false; + m_boundsValid = false; + + m_lastRenderFrame = -1000; + for(int i=0; i < KRENGINE_NODE_ATTRIBUTE_COUNT; i++) { + m_animation_mask[i] = false; + } +} + +KRNode::~KRNode() { + + + while(m_childNodes.size() > 0) { + delete *m_childNodes.begin(); + } + + for(std::set::iterator itr = m_behaviors.begin(); itr != m_behaviors.end(); itr++) { + delete *itr; + } + m_behaviors.clear(); + + if(m_parentNode) { + m_parentNode->childDeleted(this); + } + + getScene().notify_sceneGraphDelete(this); + +} + +void KRNode::setScaleCompensation(bool scale_compensation) +{ + if(m_scale_compensation != scale_compensation) { + m_scale_compensation = scale_compensation; + invalidateModelMatrix(); + invalidateBindPoseMatrix(); + } +} +bool KRNode::getScaleCompensation() +{ + return m_scale_compensation; +} + +void KRNode::childDeleted(KRNode *child_node) +{ + m_childNodes.erase(child_node); + invalidateBounds(); + getScene().notify_sceneGraphModify(this); +} + +void KRNode::addChild(KRNode *child) { + assert(child->m_parentNode == NULL); + child->m_parentNode = this; + m_childNodes.insert(child); + child->setLODVisibility(m_lod_visible); // Child node inherits LOD visibility status from parent +} + +tinyxml2::XMLElement *KRNode::saveXML(tinyxml2::XMLNode *parent) { + tinyxml2::XMLDocument *doc = parent->GetDocument(); + tinyxml2::XMLElement *e = doc->NewElement(getElementName().c_str()); + tinyxml2::XMLNode *n = parent->InsertEndChild(e); + e->SetAttribute("name", m_name.c_str()); + kraken::setXMLAttribute("translate", e, m_localTranslation, Vector3::Zero()); + kraken::setXMLAttribute("scale", e, m_localScale, Vector3::One()); + kraken::setXMLAttribute("rotate", e, (m_localRotation * (180.0f / M_PI)), Vector3::Zero()); + kraken::setXMLAttribute("rotate_offset", e, m_rotationOffset, Vector3::Zero()); + kraken::setXMLAttribute("scale_offset", e, m_scalingOffset, Vector3::Zero()); + kraken::setXMLAttribute("rotate_pivot", e, m_rotationPivot, Vector3::Zero()); + kraken::setXMLAttribute("scale_pivot", e, m_scalingPivot, Vector3::Zero()); + kraken::setXMLAttribute("pre_rotate", e, (m_preRotation * (180.0f / M_PI)), Vector3::Zero()); + kraken::setXMLAttribute("post_rotate", e, (m_postRotation * (180.0f / M_PI)), Vector3::Zero()); + + for(std::set::iterator itr=m_childNodes.begin(); itr != m_childNodes.end(); ++itr) { + KRNode *child = (*itr); + child->saveXML(n); + } + return e; +} + +void KRNode::loadXML(tinyxml2::XMLElement *e) { + m_name = e->Attribute("name"); + m_localTranslation = kraken::getXMLAttribute("translate", e, Vector3::Zero()); + m_localScale = kraken::getXMLAttribute("scale", e, Vector3::One()); + m_localRotation = kraken::getXMLAttribute("rotate", e, Vector3::Zero()); + m_localRotation *= M_PI / 180.0f; // Convert degrees to radians + m_preRotation = kraken::getXMLAttribute("pre_rotate", e, Vector3::Zero()); + m_preRotation *= M_PI / 180.0f; // Convert degrees to radians + m_postRotation = kraken::getXMLAttribute("post_rotate", e, Vector3::Zero()); + m_postRotation *= M_PI / 180.0f; // Convert degrees to radians + + m_rotationOffset = kraken::getXMLAttribute("rotate_offset", e, Vector3::Zero()); + m_scalingOffset = kraken::getXMLAttribute("scale_offset", e, Vector3::Zero()); + m_rotationPivot = kraken::getXMLAttribute("rotate_pivot", e, Vector3::Zero()); + m_scalingPivot = kraken::getXMLAttribute("scale_pivot", e, Vector3::Zero()); + + m_initialLocalTranslation = m_localTranslation; + m_initialLocalScale = m_localScale; + m_initialLocalRotation = m_localRotation; + + m_initialRotationOffset = m_rotationOffset; + m_initialScalingOffset = m_scalingOffset; + m_initialRotationPivot = m_rotationPivot; + m_initialScalingPivot = m_scalingPivot; + m_initialPreRotation = m_preRotation; + m_initialPostRotation = m_postRotation; + + m_bindPoseMatrixValid = false; + m_activePoseMatrixValid = false; + m_inverseBindPoseMatrixValid = false; + m_modelMatrixValid = false; + m_inverseModelMatrixValid = false; + + for(tinyxml2::XMLElement *child_element=e->FirstChildElement(); child_element != NULL; child_element = child_element->NextSiblingElement()) { + const char *szElementName = child_element->Name(); + if(strcmp(szElementName, "behavior") == 0) { + KRBehavior *behavior = KRBehavior::LoadXML(this, child_element); + if(behavior) { + addBehavior(behavior); + behavior->init(); + } + } else { + KRNode *child_node = KRNode::LoadXML(getScene(), child_element); + + if(child_node) { + addChild(child_node); + } + } + } +} + +void KRNode::setLocalTranslation(const Vector3 &v, bool set_original) { + m_localTranslation = v; + if(set_original) { + m_initialLocalTranslation = v; + invalidateBindPoseMatrix(); + } + invalidateModelMatrix(); +} + +void KRNode::setWorldTranslation(const Vector3 &v) +{ + if(m_parentNode) { + setLocalTranslation(Matrix4::Dot(m_parentNode->getInverseModelMatrix(), v)); + } else { + setLocalTranslation(v); + } +} + + +void KRNode::setWorldRotation(const Vector3 &v) +{ + if(m_parentNode) { + setLocalRotation((Quaternion::Create(v) * -m_parentNode->getWorldRotation()).eulerXYZ()); + setPreRotation(Vector3::Zero()); + setPostRotation(Vector3::Zero()); + } else { + setLocalRotation(v); + setPreRotation(Vector3::Zero()); + setPostRotation(Vector3::Zero()); + } +} + + +void KRNode::setWorldScale(const Vector3 &v) +{ + if(m_parentNode) { + setLocalScale(Matrix4::DotNoTranslate(m_parentNode->getInverseModelMatrix(), v)); + } else { + setLocalScale(v); + } +} + +void KRNode::setLocalScale(const Vector3 &v, bool set_original) { + m_localScale = v; + if(set_original) { + m_initialLocalScale = v; + invalidateBindPoseMatrix(); + } + invalidateModelMatrix(); +} + +void KRNode::setLocalRotation(const Vector3 &v, bool set_original) { + m_localRotation = v; + if(set_original) { + m_initialLocalRotation = v; + invalidateBindPoseMatrix(); + } + invalidateModelMatrix(); +} + + +void KRNode::setRotationOffset(const Vector3 &v, bool set_original) +{ + m_rotationOffset = v; + if(set_original) { + m_initialRotationOffset = v; + invalidateBindPoseMatrix(); + } + invalidateModelMatrix(); +} + +void KRNode::setScalingOffset(const Vector3 &v, bool set_original) +{ + m_scalingOffset = v; + if(set_original) { + m_initialScalingOffset = v; + invalidateBindPoseMatrix(); + } + invalidateModelMatrix(); +} + +void KRNode::setRotationPivot(const Vector3 &v, bool set_original) +{ + m_rotationPivot = v; + if(set_original) { + m_initialRotationPivot = v; + invalidateBindPoseMatrix(); + } + invalidateModelMatrix(); +} +void KRNode::setScalingPivot(const Vector3 &v, bool set_original) +{ + m_scalingPivot = v; + if(set_original) { + m_initialScalingPivot = v; + invalidateBindPoseMatrix(); + } + invalidateModelMatrix(); +} +void KRNode::setPreRotation(const Vector3 &v, bool set_original) +{ + m_preRotation = v; + if(set_original) { + m_initialPreRotation = v; + invalidateBindPoseMatrix(); + } + invalidateModelMatrix(); +} +void KRNode::setPostRotation(const Vector3 &v, bool set_original) +{ + m_postRotation = v; + if(set_original) { + m_initialPostRotation = v; + invalidateBindPoseMatrix(); + } + invalidateModelMatrix(); +} + +const Vector3 &KRNode::getRotationOffset() +{ + return m_rotationOffset; +} +const Vector3 &KRNode::getScalingOffset() +{ + return m_scalingOffset; +} +const Vector3 &KRNode::getRotationPivot() +{ + return m_rotationPivot; +} +const Vector3 &KRNode::getScalingPivot() +{ + return m_scalingPivot; +} +const Vector3 &KRNode::getPreRotation() +{ + return m_preRotation; +} +const Vector3 &KRNode::getPostRotation() +{ + return m_postRotation; +} +const Vector3 &KRNode::getInitialRotationOffset() +{ + return m_initialRotationOffset; +} +const Vector3 &KRNode::getInitialScalingOffset() +{ + return m_initialScalingOffset; +} +const Vector3 &KRNode::getInitialRotationPivot() +{ + return m_initialRotationPivot; +} +const Vector3 &KRNode::getInitialScalingPivot() +{ + return m_initialScalingPivot; +} +const Vector3 &KRNode::getInitialPreRotation() +{ + return m_initialPreRotation; +} +const Vector3 &KRNode::getInitialPostRotation() +{ + return m_initialPostRotation; +} + +const Vector3 &KRNode::getLocalTranslation() { + return m_localTranslation; +} +const Vector3 &KRNode::getLocalScale() { + return m_localScale; +} +const Vector3 &KRNode::getLocalRotation() { + return m_localRotation; +} + +const Vector3 &KRNode::getInitialLocalTranslation() { + return m_initialLocalTranslation; +} +const Vector3 &KRNode::getInitialLocalScale() { + return m_initialLocalScale; +} +const Vector3 &KRNode::getInitialLocalRotation() { + return m_initialLocalRotation; +} + +const Vector3 KRNode::getWorldTranslation() { + return localToWorld(Vector3::Zero()); +} + +const Vector3 KRNode::getWorldScale() { + return Matrix4::DotNoTranslate(getModelMatrix(), m_localScale); +} + +std::string KRNode::getElementName() { + return "node"; +} + +KRNode *KRNode::LoadXML(KRScene &scene, tinyxml2::XMLElement *e) { + KRNode *new_node = NULL; + const char *szElementName = e->Name(); + const char *szName = e->Attribute("name"); + if(strcmp(szElementName, "node") == 0) { + new_node = new KRNode(scene, szName); + } else if(strcmp(szElementName, "lod_set") == 0) { + new_node = new KRLODSet(scene, szName); + } else if(strcmp(szElementName, "lod_group") == 0) { + new_node = new KRLODGroup(scene, szName); + } else if(strcmp(szElementName, "point_light") == 0) { + new_node = new KRPointLight(scene, szName); + } else if(strcmp(szElementName, "directional_light") == 0) { + new_node = new KRDirectionalLight(scene, szName); + } else if(strcmp(szElementName, "spot_light") == 0) { + new_node = new KRSpotLight(scene, szName); + } else if(strcmp(szElementName, "particles_newtonian") == 0) { + new_node = new KRParticleSystemNewtonian(scene, szName); + } else if(strcmp(szElementName, "sprite") == 0) { + new_node = new KRSprite(scene, szName); + } else if(strcmp(szElementName, "model") == 0) { + float lod_min_coverage = 0.0f; + if(e->QueryFloatAttribute("lod_min_coverage", &lod_min_coverage) != tinyxml2::XML_SUCCESS) { + lod_min_coverage = 0.0f; + } + bool receives_shadow = true; + if(e->QueryBoolAttribute("receives_shadow", &receives_shadow) != tinyxml2::XML_SUCCESS) { + receives_shadow = true; + } + bool faces_camera = false; + if(e->QueryBoolAttribute("faces_camera", &faces_camera) != tinyxml2::XML_SUCCESS) { + faces_camera = false; + } + float rim_power = 0.0f; + if(e->QueryFloatAttribute("rim_power", &rim_power) != tinyxml2::XML_SUCCESS) { + rim_power = 0.0f; + } + Vector3 rim_color = Vector3::Zero(); + rim_color = kraken::getXMLAttribute("rim_color", e, Vector3::Zero()); + new_node = new KRModel(scene, szName, e->Attribute("mesh"), e->Attribute("light_map"), lod_min_coverage, receives_shadow, faces_camera, rim_color, rim_power); + } else if(strcmp(szElementName, "collider") == 0) { + new_node = new KRCollider(scene, szName, e->Attribute("mesh"), 65535, 1.0f); + } else if(strcmp(szElementName, "bone") == 0) { + new_node = new KRBone(scene, szName); + } else if(strcmp(szElementName, "locator") == 0) { + new_node = new KRLocator(scene, szName); + } else if(strcmp(szElementName, "audio_source") == 0) { + new_node = new KRAudioSource(scene, szName); + } else if(strcmp(szElementName, "ambient_zone") == 0) { + new_node = new KRAmbientZone(scene, szName); + } else if(strcmp(szElementName, "reverb_zone") == 0) { + new_node = new KRReverbZone(scene, szName); + } else if(strcmp(szElementName, "camera") == 0) { + new_node = new KRCamera(scene, szName); + } + + if(new_node) { + new_node->loadXML(e); + } + + return new_node; +} + +void KRNode::render(KRCamera *pCamera, std::vector &point_lights, std::vector &directional_lights, std::vector&spot_lights, const KRViewport &viewport, RenderPass renderPass) +{ + if(m_lod_visible <= LOD_VISIBILITY_PRESTREAM) return; + + m_lastRenderFrame = getContext().getCurrentFrame(); +} + +const std::set &KRNode::getChildren() { + return m_childNodes; +} + +KRNode *KRNode::getParent() { + return m_parentNode; +} + +const std::string &KRNode::getName() const { + return m_name; +} + +KRScene &KRNode::getScene() { + return *m_pScene; +} + +AABB KRNode::getBounds() { + if(!m_boundsValid) { + AABB bounds = AABB::Zero(); + + bool first_child = true; + for(std::set::iterator itr=m_childNodes.begin(); itr != m_childNodes.end(); ++itr) { + KRNode *child = (*itr); + if(child->getBounds() != AABB::Zero()) { + if(first_child) { + first_child = false; + bounds = child->getBounds(); + } else { + bounds.encapsulate(child->getBounds()); + } + } + } + + m_bounds = bounds; + m_boundsValid = true; + } + return m_bounds; +} + +void KRNode::invalidateModelMatrix() +{ + m_modelMatrixValid = false; + m_activePoseMatrixValid = false; + m_inverseModelMatrixValid = false; + for(std::set::iterator itr=m_childNodes.begin(); itr != m_childNodes.end(); ++itr) { + KRNode *child = (*itr); + child->invalidateModelMatrix(); + } + + invalidateBounds(); + getScene().notify_sceneGraphModify(this); +} + +void KRNode::invalidateBindPoseMatrix() +{ + m_bindPoseMatrixValid = false; + m_inverseBindPoseMatrixValid = false; + for(std::set::iterator itr=m_childNodes.begin(); itr != m_childNodes.end(); ++itr) { + KRNode *child = (*itr); + child->invalidateBindPoseMatrix(); + } +} + +const Matrix4 &KRNode::getModelMatrix() +{ + + if(!m_modelMatrixValid) { + m_modelMatrix = Matrix4(); + + bool parent_is_bone = false; + if(dynamic_cast(m_parentNode)) { + parent_is_bone = true; + } + + if(getScaleCompensation() && parent_is_bone) { + + + // WorldTransform = ParentWorldTransform * T * Roff * Rp * Rpre * R * Rpost * Rp-1 * Soff * Sp * S * Sp-1 + m_modelMatrix = Matrix4::Translation(-m_scalingPivot) + * Matrix4::Scaling(m_localScale) + * Matrix4::Translation(m_scalingPivot) + * Matrix4::Translation(m_scalingOffset) + * Matrix4::Translation(-m_rotationPivot) + //* (Quaternion(m_postRotation) * Quaternion(m_localRotation) * Quaternion(m_preRotation)).rotationMatrix() + * Matrix4::Rotation(m_postRotation) + * Matrix4::Rotation(m_localRotation) + * Matrix4::Rotation(m_preRotation) + * Matrix4::Translation(m_rotationPivot) + * Matrix4::Translation(m_rotationOffset); + + if(m_parentNode) { + + m_modelMatrix.rotate(m_parentNode->getWorldRotation()); + m_modelMatrix.translate(Matrix4::Dot(m_parentNode->getModelMatrix(), m_localTranslation)); + } else { + m_modelMatrix.translate(m_localTranslation); + } + } else { + + // WorldTransform = ParentWorldTransform * T * Roff * Rp * Rpre * R * Rpost * Rp-1 * Soff * Sp * S * Sp-1 + m_modelMatrix = Matrix4::Translation(-m_scalingPivot) + * Matrix4::Scaling(m_localScale) + * Matrix4::Translation(m_scalingPivot) + * Matrix4::Translation(m_scalingOffset) + * Matrix4::Translation(-m_rotationPivot) + //* (Quaternion(m_postRotation) * Quaternion(m_localRotation) * Quaternion(m_preRotation)).rotationMatrix() + * Matrix4::Rotation(m_postRotation) + * Matrix4::Rotation(m_localRotation) + * Matrix4::Rotation(m_preRotation) + * Matrix4::Translation(m_rotationPivot) + * Matrix4::Translation(m_rotationOffset) + * Matrix4::Translation(m_localTranslation); + + if(m_parentNode) { + m_modelMatrix *= m_parentNode->getModelMatrix(); + } + } + + m_modelMatrixValid = true; + + } + return m_modelMatrix; +} + +const Matrix4 &KRNode::getBindPoseMatrix() +{ + if(!m_bindPoseMatrixValid) { + m_bindPoseMatrix = Matrix4(); + + bool parent_is_bone = false; + if(dynamic_cast(m_parentNode)) { + parent_is_bone = true; + } + + if(getScaleCompensation() && parent_is_bone) { + m_bindPoseMatrix = Matrix4::Translation(-m_initialScalingPivot) + * Matrix4::Scaling(m_initialLocalScale) + * Matrix4::Translation(m_initialScalingPivot) + * Matrix4::Translation(m_initialScalingOffset) + * Matrix4::Translation(-m_initialRotationPivot) + //* (Quaternion(m_initialPostRotation) * Quaternion(m_initialLocalRotation) * Quaternion(m_initialPreRotation)).rotationMatrix() + * Matrix4::Rotation(m_initialPostRotation) + * Matrix4::Rotation(m_initialLocalRotation) + * Matrix4::Rotation(m_initialPreRotation) + * Matrix4::Translation(m_initialRotationPivot) + * Matrix4::Translation(m_initialRotationOffset); + //m_bindPoseMatrix.translate(m_localTranslation); + if(m_parentNode) { + + m_bindPoseMatrix.rotate(m_parentNode->getBindPoseWorldRotation()); + m_bindPoseMatrix.translate(Matrix4::Dot(m_parentNode->getBindPoseMatrix(), m_localTranslation)); + } else { + m_bindPoseMatrix.translate(m_localTranslation); + } + } else { + + // WorldTransform = ParentWorldTransform * T * Roff * Rp * Rpre * R * Rpost * Rp-1 * Soff * Sp * S * Sp-1 + + m_bindPoseMatrix = Matrix4::Translation(-m_initialScalingPivot) + * Matrix4::Scaling(m_initialLocalScale) + * Matrix4::Translation(m_initialScalingPivot) + * Matrix4::Translation(m_initialScalingOffset) + * Matrix4::Translation(-m_initialRotationPivot) + // * (Quaternion(m_initialPostRotation) * Quaternion(m_initialLocalRotation) * Quaternion(m_initialPreRotation)).rotationMatrix() + * Matrix4::Rotation(m_initialPostRotation) + * Matrix4::Rotation(m_initialLocalRotation) + * Matrix4::Rotation(m_initialPreRotation) + * Matrix4::Translation(m_initialRotationPivot) + * Matrix4::Translation(m_initialRotationOffset) + * Matrix4::Translation(m_initialLocalTranslation); + + if(m_parentNode && parent_is_bone) { + + m_bindPoseMatrix *= m_parentNode->getBindPoseMatrix(); + } + } + + m_bindPoseMatrixValid = true; + + } + return m_bindPoseMatrix; +} + +const Matrix4 &KRNode::getActivePoseMatrix() +{ + + if(!m_activePoseMatrixValid) { + m_activePoseMatrix = Matrix4(); + + bool parent_is_bone = false; + if(dynamic_cast(m_parentNode)) { + parent_is_bone = true; + } + + if(getScaleCompensation() && parent_is_bone) { + m_activePoseMatrix= Matrix4::Translation(-m_scalingPivot) + * Matrix4::Scaling(m_localScale) + * Matrix4::Translation(m_scalingPivot) + * Matrix4::Translation(m_scalingOffset) + * Matrix4::Translation(-m_rotationPivot) + * Matrix4::Rotation(m_postRotation) + * Matrix4::Rotation(m_localRotation) + * Matrix4::Rotation(m_preRotation) + * Matrix4::Translation(m_rotationPivot) + * Matrix4::Translation(m_rotationOffset); + + if(m_parentNode) { + + m_activePoseMatrix.rotate(m_parentNode->getActivePoseWorldRotation()); + m_activePoseMatrix.translate(Matrix4::Dot(m_parentNode->getActivePoseMatrix(), m_localTranslation)); + } else { + m_activePoseMatrix.translate(m_localTranslation); + } + } else { + + // WorldTransform = ParentWorldTransform * T * Roff * Rp * Rpre * R * Rpost * Rp-1 * Soff * Sp * S * Sp-1 + m_activePoseMatrix = Matrix4::Translation(-m_scalingPivot) + * Matrix4::Scaling(m_localScale) + * Matrix4::Translation(m_scalingPivot) + * Matrix4::Translation(m_scalingOffset) + * Matrix4::Translation(-m_rotationPivot) + * Matrix4::Rotation(m_postRotation) + * Matrix4::Rotation(m_localRotation) + * Matrix4::Rotation(m_preRotation) + * Matrix4::Translation(m_rotationPivot) + * Matrix4::Translation(m_rotationOffset) + * Matrix4::Translation(m_localTranslation); + + + if(m_parentNode && parent_is_bone) { + m_activePoseMatrix *= m_parentNode->getActivePoseMatrix(); + } + } + + m_activePoseMatrixValid = true; + + } + return m_activePoseMatrix; + +} + +const Quaternion KRNode::getWorldRotation() { + Quaternion world_rotation = Quaternion::Create(m_postRotation) * Quaternion::Create(m_localRotation) * Quaternion::Create(m_preRotation); + if(m_parentNode) { + world_rotation = world_rotation * m_parentNode->getWorldRotation(); + } + return world_rotation; +} + +const Quaternion KRNode::getBindPoseWorldRotation() { + Quaternion world_rotation = Quaternion::Create(m_initialPostRotation) * Quaternion::Create(m_initialLocalRotation) * Quaternion::Create(m_initialPreRotation); + if(dynamic_cast(m_parentNode)) { + world_rotation = world_rotation * m_parentNode->getBindPoseWorldRotation(); + } + return world_rotation; +} + +const Quaternion KRNode::getActivePoseWorldRotation() { + Quaternion world_rotation = Quaternion::Create(m_postRotation) * Quaternion::Create(m_localRotation) * Quaternion::Create(m_preRotation); + if(dynamic_cast(m_parentNode)) { + world_rotation = world_rotation * m_parentNode->getActivePoseWorldRotation(); + } + return world_rotation; +} + +const Matrix4 &KRNode::getInverseModelMatrix() +{ + if(!m_inverseModelMatrixValid) { + m_inverseModelMatrix = Matrix4::Invert(getModelMatrix()); + } + return m_inverseModelMatrix; +} + +const Matrix4 &KRNode::getInverseBindPoseMatrix() +{ + if(!m_inverseBindPoseMatrixValid ) { + m_inverseBindPoseMatrix = Matrix4::Invert(getBindPoseMatrix()); + m_inverseBindPoseMatrixValid = true; + } + return m_inverseBindPoseMatrix; +} + +void KRNode::physicsUpdate(float deltaTime) +{ + const long MIN_DISPLAY_FRAMES = 10; + bool visible = m_lastRenderFrame + MIN_DISPLAY_FRAMES >= getContext().getCurrentFrame(); + for(std::set::iterator itr=m_behaviors.begin(); itr != m_behaviors.end(); itr++) { + (*itr)->update(deltaTime); + if(visible) { + (*itr)->visibleUpdate(deltaTime); + } + } +} + +bool KRNode::hasPhysics() +{ + return m_behaviors.size() > 0; +} + +void KRNode::SetAttribute(node_attribute_type attrib, float v) +{ + if(m_animation_mask[attrib]) return; + + const float DEGREES_TO_RAD = M_PI / 180.0f; + + //printf("%s - ", m_name.c_str()); + switch(attrib) { + case KRENGINE_NODE_ATTRIBUTE_TRANSLATE_X: + setLocalTranslation(Vector3::Create(v, m_localTranslation.y, m_localTranslation.z)); + break; + case KRENGINE_NODE_ATTRIBUTE_TRANSLATE_Y: + setLocalTranslation(Vector3::Create(m_localTranslation.x, v, m_localTranslation.z)); + break; + case KRENGINE_NODE_ATTRIBUTE_TRANSLATE_Z: + setLocalTranslation(Vector3::Create(m_localTranslation.x, m_localTranslation.y, v)); + break; + case KRENGINE_NODE_ATTRIBUTE_SCALE_X: + setLocalScale(Vector3::Create(v, m_localScale.y, m_localScale.z)); + break; + case KRENGINE_NODE_ATTRIBUTE_SCALE_Y: + setLocalScale(Vector3::Create(m_localScale.x, v, m_localScale.z)); + break; + case KRENGINE_NODE_ATTRIBUTE_SCALE_Z: + setLocalScale(Vector3::Create(m_localScale.x, m_localScale.y, v)); + break; + case KRENGINE_NODE_ATTRIBUTE_ROTATE_X: + setLocalRotation(Vector3::Create(v * DEGREES_TO_RAD, m_localRotation.y, m_localRotation.z)); + break; + case KRENGINE_NODE_ATTRIBUTE_ROTATE_Y: + setLocalRotation(Vector3::Create(m_localRotation.x, v * DEGREES_TO_RAD, m_localRotation.z)); + break; + case KRENGINE_NODE_ATTRIBUTE_ROTATE_Z: + setLocalRotation(Vector3::Create(m_localRotation.x, m_localRotation.y, v * DEGREES_TO_RAD)); + break; + + + case KRENGINE_NODE_ATTRIBUTE_PRE_ROTATION_X: + setPreRotation(Vector3::Create(v * DEGREES_TO_RAD, m_preRotation.y, m_preRotation.z)); + break; + case KRENGINE_NODE_ATTRIBUTE_PRE_ROTATION_Y: + setPreRotation(Vector3::Create(m_preRotation.x, v * DEGREES_TO_RAD, m_preRotation.z)); + break; + case KRENGINE_NODE_ATTRIBUTE_PRE_ROTATION_Z: + setPreRotation(Vector3::Create(m_preRotation.x, m_preRotation.y, v * DEGREES_TO_RAD)); + break; + case KRENGINE_NODE_ATTRIBUTE_POST_ROTATION_X: + setPostRotation(Vector3::Create(v * DEGREES_TO_RAD, m_postRotation.y, m_postRotation.z)); + break; + case KRENGINE_NODE_ATTRIBUTE_POST_ROTATION_Y: + setPostRotation(Vector3::Create(m_postRotation.x, v * DEGREES_TO_RAD, m_postRotation.z)); + break; + case KRENGINE_NODE_ATTRIBUTE_POST_ROTATION_Z: + setPostRotation(Vector3::Create(m_postRotation.x, m_postRotation.y, v * DEGREES_TO_RAD)); + break; + case KRENGINE_NODE_ATTRIBUTE_ROTATION_PIVOT_X: + setRotationPivot(Vector3::Create(v, m_rotationPivot.y, m_rotationPivot.z)); + break; + case KRENGINE_NODE_ATTRIBUTE_ROTATION_PIVOT_Y: + setRotationPivot(Vector3::Create(m_rotationPivot.x, v, m_rotationPivot.z)); + break; + case KRENGINE_NODE_ATTRIBUTE_ROTATION_PIVOT_Z: + setRotationPivot(Vector3::Create(m_rotationPivot.x, m_rotationPivot.y, v)); + break; + case KRENGINE_NODE_ATTRIBUTE_SCALE_PIVOT_X: + setScalingPivot(Vector3::Create(v, m_scalingPivot.y, m_scalingPivot.z)); + break; + case KRENGINE_NODE_ATTRIBUTE_SCALE_PIVOT_Y: + setScalingPivot(Vector3::Create(m_scalingPivot.x, v, m_scalingPivot.z)); + break; + case KRENGINE_NODE_ATTRIBUTE_SCALE_PIVOT_Z: + setScalingPivot(Vector3::Create(m_scalingPivot.x, m_scalingPivot.y, v)); + break; + case KRENGINE_NODE_ATTRIBUTE_ROTATE_OFFSET_X: + setRotationOffset(Vector3::Create(v, m_rotationOffset.y, m_rotationOffset.z)); + break; + case KRENGINE_NODE_ATTRIBUTE_ROTATE_OFFSET_Y: + setRotationOffset(Vector3::Create(m_rotationOffset.x, v, m_rotationOffset.z)); + break; + case KRENGINE_NODE_ATTRIBUTE_ROTATE_OFFSET_Z: + setRotationOffset(Vector3::Create(m_rotationOffset.x, m_rotationOffset.y, v)); + break; + case KRENGINE_NODE_SCALE_OFFSET_X: + setScalingOffset(Vector3::Create(v, m_scalingOffset.y, m_scalingOffset.z)); + break; + case KRENGINE_NODE_SCALE_OFFSET_Y: + setScalingOffset(Vector3::Create(m_scalingOffset.x, v, m_scalingOffset.z)); + break; + case KRENGINE_NODE_SCALE_OFFSET_Z: + setScalingOffset(Vector3::Create(m_scalingOffset.x, m_scalingOffset.y, v)); + break; + case KRENGINE_NODE_ATTRIBUTE_NONE: + case KRENGINE_NODE_ATTRIBUTE_COUNT: + // Suppress warnings + break; + } +} + +void KRNode::setAnimationEnabled(node_attribute_type attrib, bool enable) +{ + m_animation_mask[attrib] = !enable; +} +bool KRNode::getAnimationEnabled(node_attribute_type attrib) const +{ + return !m_animation_mask[attrib]; +} + +void KRNode::removeFromOctreeNodes() +{ + for(std::set::iterator itr=m_octree_nodes.begin(); itr != m_octree_nodes.end(); itr++) { + KROctreeNode *octree_node = *itr; + octree_node->remove(this); + + // FINDME, TODO - This should be moved to the KROctree class + while(octree_node) { + octree_node->trim(); + if(octree_node->isEmpty()) { + octree_node = octree_node->getParent(); + } else { + octree_node = NULL; + } + } + } + m_octree_nodes.clear(); +} + +void KRNode::addToOctreeNode(KROctreeNode *octree_node) +{ + m_octree_nodes.insert(octree_node); +} + +void KRNode::updateLODVisibility(const KRViewport &viewport) +{ + if(m_lod_visible >= LOD_VISIBILITY_PRESTREAM) { + for(std::set::iterator itr=m_childNodes.begin(); itr != m_childNodes.end(); ++itr) { + (*itr)->updateLODVisibility(viewport); + } + } +} + +void KRNode::setLODVisibility(KRNode::LodVisibility lod_visibility) +{ + if(m_lod_visible != lod_visibility) { + if(m_lod_visible == LOD_VISIBILITY_HIDDEN && lod_visibility >= LOD_VISIBILITY_PRESTREAM) { + getScene().notify_sceneGraphCreate(this); + } else if(m_lod_visible >= LOD_VISIBILITY_PRESTREAM && lod_visibility == LOD_VISIBILITY_HIDDEN) { + getScene().notify_sceneGraphDelete(this); + } + + m_lod_visible = lod_visibility; + + for(std::set::iterator itr=m_childNodes.begin(); itr != m_childNodes.end(); ++itr) { + (*itr)->setLODVisibility(lod_visibility); + } + } +} + +KRNode::LodVisibility KRNode::getLODVisibility() +{ + return m_lod_visible; +} + +const Vector3 KRNode::localToWorld(const Vector3 &local_point) +{ + return Matrix4::Dot(getModelMatrix(), local_point); +} + +const Vector3 KRNode::worldToLocal(const Vector3 &world_point) +{ + return Matrix4::Dot(getInverseModelMatrix(), world_point); +} + +void KRNode::addBehavior(KRBehavior *behavior) +{ + m_behaviors.insert(behavior); + behavior->__setNode(this); + getScene().notify_sceneGraphModify(this); +} + +std::set &KRNode::getBehaviors() +{ + return m_behaviors; +} + +kraken_stream_level KRNode::getStreamLevel(const KRViewport &viewport) +{ + kraken_stream_level stream_level = kraken_stream_level::STREAM_LEVEL_IN_HQ; + + for(std::set::iterator itr=m_childNodes.begin(); itr != m_childNodes.end(); ++itr) { + stream_level = KRMIN(stream_level, (*itr)->getStreamLevel(viewport)); + } + + return stream_level; +} + +void KRNode::invalidateBounds() const +{ + m_boundsValid = false; + if(m_parentNode) { + m_parentNode->invalidateBounds(); + } +} diff --git a/kraken/KROctree.cpp b/kraken/KROctree.cpp index 4451435..b6b200f 100755 --- a/kraken/KROctree.cpp +++ b/kraken/KROctree.cpp @@ -1,156 +1,156 @@ -// -// KROctree.cpp -// KREngine -// -// Created by Kearwood Gilbert on 2012-08-29. -// Copyright (c) 2012 Kearwood Software. All rights reserved. -// - -#include "public/kraken.h" -#include "KROctree.h" -#include "KRNode.h" -#include "KRCollider.h" - -KROctree::KROctree() -{ - m_pRootNode = NULL; -} - -KROctree::~KROctree() -{ - if(m_pRootNode) { - delete m_pRootNode; - } -} - -void KROctree::add(KRNode *pNode) -{ - AABB nodeBounds = pNode->getBounds(); - if(nodeBounds == AABB::Zero()) { - // This item is not visible, don't add it to the octree or outer scene nodes - } else if(nodeBounds == AABB::Infinite()) { - // This item is infinitely large; we track it separately - m_outerSceneNodes.insert(pNode); - } else { - if(m_pRootNode == NULL) { - // First item inserted, create a node large enough to fit it - m_pRootNode = new KROctreeNode(NULL, nodeBounds); - m_pRootNode->add(pNode); - } else { - // Keep encapsulating the root node until the new root contains the inserted node - bool bInsideRoot = false; - while(!bInsideRoot) { - AABB rootBounds = m_pRootNode->getBounds(); - Vector3 rootSize = rootBounds.size(); - if(nodeBounds.min.x < rootBounds.min.x || nodeBounds.min.y < rootBounds.min.y || nodeBounds.min.z < rootBounds.min.z) { - m_pRootNode = new KROctreeNode(NULL, AABB(rootBounds.min - rootSize, rootBounds.max), 7, m_pRootNode); - } else if(nodeBounds.max.x > rootBounds.max.x || nodeBounds.max.y > rootBounds.max.y || nodeBounds.max.z > rootBounds.max.z) { - m_pRootNode = new KROctreeNode(NULL, AABB(rootBounds.min, rootBounds.max + rootSize), 0, m_pRootNode); - } else { - bInsideRoot = true; - } - } - m_pRootNode->add(pNode); - } - } -} - -void KROctree::remove(KRNode *pNode) -{ - if(!m_outerSceneNodes.erase(pNode)) { - if(m_pRootNode) { - pNode->removeFromOctreeNodes(); - } - } - - shrink(); -} - -void KROctree::update(KRNode *pNode) -{ - // TODO: This may be more efficient as an incremental operation rather than removing and re-adding the node - remove(pNode); - add(pNode); - shrink(); -} - -void KROctree::shrink() -{ - if(m_pRootNode) { - while(m_pRootNode->canShrinkRoot()) { - KROctreeNode *newRoot = m_pRootNode->stripChild(); - delete m_pRootNode; - m_pRootNode = newRoot; - if(m_pRootNode == NULL) return; - } - } -} - -KROctreeNode *KROctree::getRootNode() -{ - return m_pRootNode; -} - -std::set &KROctree::getOuterSceneNodes() -{ - return m_outerSceneNodes; -} - - -bool KROctree::lineCast(const Vector3 &v0, const Vector3 &v1, HitInfo &hitinfo, unsigned int layer_mask) -{ - bool hit_found = false; - std::vector outer_colliders; - - for(std::set::iterator outer_nodes_itr=m_outerSceneNodes.begin(); outer_nodes_itr != m_outerSceneNodes.end(); outer_nodes_itr++) { - KRCollider *collider = dynamic_cast(*outer_nodes_itr); - if(collider) { - outer_colliders.push_back(collider); - } - } - for(std::vector::iterator itr=outer_colliders.begin(); itr != outer_colliders.end(); itr++) { - if((*itr)->lineCast(v0, v1, hitinfo, layer_mask)) hit_found = true; - } - - if(m_pRootNode) { - if(m_pRootNode->lineCast(v0, v1, hitinfo, layer_mask)) hit_found = true; - } - return hit_found; -} - -bool KROctree::rayCast(const Vector3 &v0, const Vector3 &dir, HitInfo &hitinfo, unsigned int layer_mask) -{ - bool hit_found = false; - for(std::set::iterator outer_nodes_itr=m_outerSceneNodes.begin(); outer_nodes_itr != m_outerSceneNodes.end(); outer_nodes_itr++) { - KRCollider *collider = dynamic_cast(*outer_nodes_itr); - if(collider) { - if(collider->rayCast(v0, dir, hitinfo, layer_mask)) hit_found = true; - } - } - if(m_pRootNode) { - if(m_pRootNode->rayCast(v0, dir, hitinfo, layer_mask)) hit_found = true; - } - return hit_found; -} - -bool KROctree::sphereCast(const Vector3 &v0, const Vector3 &v1, float radius, HitInfo &hitinfo, unsigned int layer_mask) -{ - bool hit_found = false; - std::vector outer_colliders; - - for(std::set::iterator outer_nodes_itr=m_outerSceneNodes.begin(); outer_nodes_itr != m_outerSceneNodes.end(); outer_nodes_itr++) { - KRCollider *collider = dynamic_cast(*outer_nodes_itr); - if(collider) { - outer_colliders.push_back(collider); - } - } - for(std::vector::iterator itr=outer_colliders.begin(); itr != outer_colliders.end(); itr++) { - if((*itr)->sphereCast(v0, v1, radius, hitinfo, layer_mask)) hit_found = true; - } - - if(m_pRootNode) { - if(m_pRootNode->sphereCast(v0, v1, radius, hitinfo, layer_mask)) hit_found = true; - } - return hit_found; -} - +// +// KROctree.cpp +// KREngine +// +// Created by Kearwood Gilbert on 2012-08-29. +// Copyright (c) 2012 Kearwood Software. All rights reserved. +// + +#include "public/kraken.h" +#include "KROctree.h" +#include "KRNode.h" +#include "KRCollider.h" + +KROctree::KROctree() +{ + m_pRootNode = NULL; +} + +KROctree::~KROctree() +{ + if(m_pRootNode) { + delete m_pRootNode; + } +} + +void KROctree::add(KRNode *pNode) +{ + AABB nodeBounds = pNode->getBounds(); + if(nodeBounds == AABB::Zero()) { + // This item is not visible, don't add it to the octree or outer scene nodes + } else if(nodeBounds == AABB::Infinite()) { + // This item is infinitely large; we track it separately + m_outerSceneNodes.insert(pNode); + } else { + if(m_pRootNode == NULL) { + // First item inserted, create a node large enough to fit it + m_pRootNode = new KROctreeNode(NULL, nodeBounds); + m_pRootNode->add(pNode); + } else { + // Keep encapsulating the root node until the new root contains the inserted node + bool bInsideRoot = false; + while(!bInsideRoot) { + AABB rootBounds = m_pRootNode->getBounds(); + Vector3 rootSize = rootBounds.size(); + if(nodeBounds.min.x < rootBounds.min.x || nodeBounds.min.y < rootBounds.min.y || nodeBounds.min.z < rootBounds.min.z) { + m_pRootNode = new KROctreeNode(NULL, AABB::Create(rootBounds.min - rootSize, rootBounds.max), 7, m_pRootNode); + } else if(nodeBounds.max.x > rootBounds.max.x || nodeBounds.max.y > rootBounds.max.y || nodeBounds.max.z > rootBounds.max.z) { + m_pRootNode = new KROctreeNode(NULL, AABB::Create(rootBounds.min, rootBounds.max + rootSize), 0, m_pRootNode); + } else { + bInsideRoot = true; + } + } + m_pRootNode->add(pNode); + } + } +} + +void KROctree::remove(KRNode *pNode) +{ + if(!m_outerSceneNodes.erase(pNode)) { + if(m_pRootNode) { + pNode->removeFromOctreeNodes(); + } + } + + shrink(); +} + +void KROctree::update(KRNode *pNode) +{ + // TODO: This may be more efficient as an incremental operation rather than removing and re-adding the node + remove(pNode); + add(pNode); + shrink(); +} + +void KROctree::shrink() +{ + if(m_pRootNode) { + while(m_pRootNode->canShrinkRoot()) { + KROctreeNode *newRoot = m_pRootNode->stripChild(); + delete m_pRootNode; + m_pRootNode = newRoot; + if(m_pRootNode == NULL) return; + } + } +} + +KROctreeNode *KROctree::getRootNode() +{ + return m_pRootNode; +} + +std::set &KROctree::getOuterSceneNodes() +{ + return m_outerSceneNodes; +} + + +bool KROctree::lineCast(const Vector3 &v0, const Vector3 &v1, HitInfo &hitinfo, unsigned int layer_mask) +{ + bool hit_found = false; + std::vector outer_colliders; + + for(std::set::iterator outer_nodes_itr=m_outerSceneNodes.begin(); outer_nodes_itr != m_outerSceneNodes.end(); outer_nodes_itr++) { + KRCollider *collider = dynamic_cast(*outer_nodes_itr); + if(collider) { + outer_colliders.push_back(collider); + } + } + for(std::vector::iterator itr=outer_colliders.begin(); itr != outer_colliders.end(); itr++) { + if((*itr)->lineCast(v0, v1, hitinfo, layer_mask)) hit_found = true; + } + + if(m_pRootNode) { + if(m_pRootNode->lineCast(v0, v1, hitinfo, layer_mask)) hit_found = true; + } + return hit_found; +} + +bool KROctree::rayCast(const Vector3 &v0, const Vector3 &dir, HitInfo &hitinfo, unsigned int layer_mask) +{ + bool hit_found = false; + for(std::set::iterator outer_nodes_itr=m_outerSceneNodes.begin(); outer_nodes_itr != m_outerSceneNodes.end(); outer_nodes_itr++) { + KRCollider *collider = dynamic_cast(*outer_nodes_itr); + if(collider) { + if(collider->rayCast(v0, dir, hitinfo, layer_mask)) hit_found = true; + } + } + if(m_pRootNode) { + if(m_pRootNode->rayCast(v0, dir, hitinfo, layer_mask)) hit_found = true; + } + return hit_found; +} + +bool KROctree::sphereCast(const Vector3 &v0, const Vector3 &v1, float radius, HitInfo &hitinfo, unsigned int layer_mask) +{ + bool hit_found = false; + std::vector outer_colliders; + + for(std::set::iterator outer_nodes_itr=m_outerSceneNodes.begin(); outer_nodes_itr != m_outerSceneNodes.end(); outer_nodes_itr++) { + KRCollider *collider = dynamic_cast(*outer_nodes_itr); + if(collider) { + outer_colliders.push_back(collider); + } + } + for(std::vector::iterator itr=outer_colliders.begin(); itr != outer_colliders.end(); itr++) { + if((*itr)->sphereCast(v0, v1, radius, hitinfo, layer_mask)) hit_found = true; + } + + if(m_pRootNode) { + if(m_pRootNode->sphereCast(v0, v1, radius, hitinfo, layer_mask)) hit_found = true; + } + return hit_found; +} + diff --git a/kraken/KROctreeNode.cpp b/kraken/KROctreeNode.cpp index 915834e..d72adea 100755 --- a/kraken/KROctreeNode.cpp +++ b/kraken/KROctreeNode.cpp @@ -1,290 +1,290 @@ -// -// KROctreeNode.cpp -// KREngine -// -// Created by Kearwood Gilbert on 2012-08-29. -// Copyright (c) 2012 Kearwood Software. All rights reserved. -// - -#include "KROctreeNode.h" -#include "KRNode.h" -#include "KRCollider.h" - -KROctreeNode::KROctreeNode(KROctreeNode *parent, const AABB &bounds) : m_bounds(bounds) -{ - m_parent = parent; - - for(int i=0; i<8; i++) m_children[i] = NULL; - - m_occlusionQuery = 0; - m_occlusionTested = false; - m_activeQuery = false; -} - -KROctreeNode::KROctreeNode(KROctreeNode *parent, const AABB &bounds, int iChild, KROctreeNode *pChild) : m_bounds(bounds) -{ - // This constructor is used when expanding the octree and replacing the root node with a new root that encapsulates it - m_parent = parent; - - for(int i=0; i<8; i++) m_children[i] = NULL; - m_children[iChild] = pChild; - pChild->m_parent = this; - - m_occlusionQuery = 0; - m_occlusionTested = false; - m_activeQuery = false; -} - -KROctreeNode::~KROctreeNode() -{ - for(int i=0; i<8; i++) { - if(m_children[i] != NULL) { - delete m_children[i]; - } - } - - if(m_occlusionTested) { - GLDEBUG(glDeleteQueriesEXT(1, &m_occlusionQuery)); - } -} - - -void KROctreeNode::beginOcclusionQuery() -{ - if(!m_occlusionTested){ - GLDEBUG(glGenQueriesEXT(1, &m_occlusionQuery)); -#if TARGET_OS_IPHONE - GLDEBUG(glBeginQueryEXT(GL_ANY_SAMPLES_PASSED_EXT, m_occlusionQuery)); -#else - GLDEBUG(glBeginQuery(GL_SAMPLES_PASSED, m_occlusionQuery)); -#endif - m_occlusionTested = true; - m_activeQuery = true; - } -} - -void KROctreeNode::endOcclusionQuery() -{ - if(m_activeQuery) { - // Only end a query if we started one -#if TARGET_OS_IPHONE - GLDEBUG(glEndQueryEXT(GL_ANY_SAMPLES_PASSED_EXT)); -#else - GLDEBUG(glEndQuery(GL_SAMPLES_PASSED)); -#endif - } -} - - -AABB KROctreeNode::getBounds() -{ - return m_bounds; -} - -void KROctreeNode::add(KRNode *pNode) -{ - int iChild = getChildIndex(pNode); - if(iChild == -1) { - m_sceneNodes.insert(pNode); - pNode->addToOctreeNode(this); - } else { - if(m_children[iChild] == NULL) { - m_children[iChild] = new KROctreeNode(this, getChildBounds(iChild)); - } - m_children[iChild]->add(pNode); - } -} - -AABB KROctreeNode::getChildBounds(int iChild) -{ - Vector3 center = m_bounds.center(); - - return AABB( - Vector3( - (iChild & 1) == 0 ? m_bounds.min.x : center.x, - (iChild & 2) == 0 ? m_bounds.min.y : center.y, - (iChild & 4) == 0 ? m_bounds.min.z : center.z), - Vector3( - (iChild & 1) == 0 ? center.x : m_bounds.max.x, - (iChild & 2) == 0 ? center.y : m_bounds.max.y, - (iChild & 4) == 0 ? center.z : m_bounds.max.z) - ); -} - -int KROctreeNode::getChildIndex(KRNode *pNode) -{ - for(int iChild=0; iChild < 8; iChild++) { - if(getChildBounds(iChild).contains(pNode->getBounds())) { - return iChild; - } - } - return -1; -} - -void KROctreeNode::trim() -{ - for(int iChild = 0; iChild < 8; iChild++) { - if(m_children[iChild]) { - if(m_children[iChild]->isEmpty()) { - delete m_children[iChild]; - m_children[iChild] = NULL; - } - } - } -} - -void KROctreeNode::remove(KRNode *pNode) -{ - m_sceneNodes.erase(pNode); -} - -void KROctreeNode::update(KRNode *pNode) -{ - -} - -bool KROctreeNode::isEmpty() const -{ - for(int i=0; i<8; i++) { - if(m_children[i]) { - return false; - } - } - return m_sceneNodes.empty(); -} - -bool KROctreeNode::canShrinkRoot() const -{ - int cChildren = 0; - for(int i=0; i<8; i++) { - if(m_children[i]) { - cChildren++; - } - } - return cChildren <= 1 && m_sceneNodes.empty(); -} - -KROctreeNode *KROctreeNode::stripChild() -{ - // Return the first found child and update its reference to NULL so that the destructor will not free it. This is used for shrinking the octree - // NOTE: The caller of this function will be responsible for freeing the child object. It is also possible to return a NULL - for(int i=0; i<8; i++) { - if(m_children[i]) { - KROctreeNode *child = m_children[i]; - child->m_parent = NULL; - m_children[i] = NULL; - return child; - } - } - return NULL; -} - - -KROctreeNode *KROctreeNode::getParent() -{ - return m_parent; -} - -KROctreeNode **KROctreeNode::getChildren() -{ - return m_children; -} - -std::set &KROctreeNode::getSceneNodes() -{ - return m_sceneNodes; -} - - -bool KROctreeNode::lineCast(const Vector3 &v0, const Vector3 &v1, HitInfo &hitinfo, unsigned int layer_mask) -{ - bool hit_found = false; - if(hitinfo.didHit() && v1 != hitinfo.getPosition()) { - // Optimization: If we already have a hit, only search for hits that are closer - hit_found = lineCast(v0, hitinfo.getPosition(), hitinfo, layer_mask); - } else { - if(getBounds().intersectsLine(v0, v1)) { - for(std::set::iterator nodes_itr=m_sceneNodes.begin(); nodes_itr != m_sceneNodes.end(); nodes_itr++) { - KRCollider *collider = dynamic_cast(*nodes_itr); - if(collider) { - if(collider->lineCast(v0, v1, hitinfo, layer_mask)) hit_found = true; - } - } - - for(int i=0; i<8; i++) { - if(m_children[i]) { - if(m_children[i]->lineCast(v0, v1, hitinfo, layer_mask)) { - hit_found = true; - } - } - } - } - } - - return hit_found; -} - -bool KROctreeNode::rayCast(const Vector3 &v0, const Vector3 &dir, HitInfo &hitinfo, unsigned int layer_mask) -{ - bool hit_found = false; - if(hitinfo.didHit()) { - // Optimization: If we already have a hit, only search for hits that are closer - hit_found = lineCast(v0, hitinfo.getPosition(), hitinfo, layer_mask); // Note: This is purposefully lineCast as opposed to RayCast - } else { - if(getBounds().intersectsRay(v0, dir)) { - for(std::set::iterator nodes_itr=m_sceneNodes.begin(); nodes_itr != m_sceneNodes.end(); nodes_itr++) { - KRCollider *collider = dynamic_cast(*nodes_itr); - if(collider) { - if(collider->rayCast(v0, dir, hitinfo, layer_mask)) hit_found = true; - } - } - - for(int i=0; i<8; i++) { - if(m_children[i]) { - if(m_children[i]->rayCast(v0, dir, hitinfo, layer_mask)) { - hit_found = true; - } - } - } - } - } - - return hit_found; -} - -bool KROctreeNode::sphereCast(const Vector3 &v0, const Vector3 &v1, float radius, HitInfo &hitinfo, unsigned int layer_mask) -{ - bool hit_found = false; - /* - // FINDME, TODO - Adapt this optimization to work with sphereCasts - - if(hitinfo.didHit() && v1 != hitinfo.getPosition()) { - // Optimization: If we already have a hit, only search for hits that are closer - hit_found = sphereCast(v0, hitinfo.getPosition(), radius, hitinfo, layer_mask); - } else { - */ - - AABB swept_bounds = AABB(Vector3(KRMIN(v0.x, v1.x) - radius, KRMIN(v0.y, v1.y) - radius, KRMIN(v0.z, v1.z) - radius), Vector3(KRMAX(v0.x, v1.x) + radius, KRMAX(v0.y, v1.y) + radius, KRMAX(v0.z, v1.z) + radius)); - // FINDME, TODO - Investigate AABB - swept sphere intersections or OBB - AABB intersections: "if(getBounds().intersectsSweptSphere(v0, v1, radius)) {" - if(getBounds().intersects(swept_bounds)) { - - for(std::set::iterator nodes_itr=m_sceneNodes.begin(); nodes_itr != m_sceneNodes.end(); nodes_itr++) { - KRCollider *collider = dynamic_cast(*nodes_itr); - if(collider) { - if(collider->sphereCast(v0, v1, radius, hitinfo, layer_mask)) hit_found = true; - } - } - - for(int i=0; i<8; i++) { - if(m_children[i]) { - if(m_children[i]->sphereCast(v0, v1, radius, hitinfo, layer_mask)) { - hit_found = true; - } - } - } - } - // } - - return hit_found; -} - +// +// KROctreeNode.cpp +// KREngine +// +// Created by Kearwood Gilbert on 2012-08-29. +// Copyright (c) 2012 Kearwood Software. All rights reserved. +// + +#include "KROctreeNode.h" +#include "KRNode.h" +#include "KRCollider.h" + +KROctreeNode::KROctreeNode(KROctreeNode *parent, const AABB &bounds) : m_bounds(bounds) +{ + m_parent = parent; + + for(int i=0; i<8; i++) m_children[i] = NULL; + + m_occlusionQuery = 0; + m_occlusionTested = false; + m_activeQuery = false; +} + +KROctreeNode::KROctreeNode(KROctreeNode *parent, const AABB &bounds, int iChild, KROctreeNode *pChild) : m_bounds(bounds) +{ + // This constructor is used when expanding the octree and replacing the root node with a new root that encapsulates it + m_parent = parent; + + for(int i=0; i<8; i++) m_children[i] = NULL; + m_children[iChild] = pChild; + pChild->m_parent = this; + + m_occlusionQuery = 0; + m_occlusionTested = false; + m_activeQuery = false; +} + +KROctreeNode::~KROctreeNode() +{ + for(int i=0; i<8; i++) { + if(m_children[i] != NULL) { + delete m_children[i]; + } + } + + if(m_occlusionTested) { + GLDEBUG(glDeleteQueriesEXT(1, &m_occlusionQuery)); + } +} + + +void KROctreeNode::beginOcclusionQuery() +{ + if(!m_occlusionTested){ + GLDEBUG(glGenQueriesEXT(1, &m_occlusionQuery)); +#if TARGET_OS_IPHONE + GLDEBUG(glBeginQueryEXT(GL_ANY_SAMPLES_PASSED_EXT, m_occlusionQuery)); +#else + GLDEBUG(glBeginQuery(GL_SAMPLES_PASSED, m_occlusionQuery)); +#endif + m_occlusionTested = true; + m_activeQuery = true; + } +} + +void KROctreeNode::endOcclusionQuery() +{ + if(m_activeQuery) { + // Only end a query if we started one +#if TARGET_OS_IPHONE + GLDEBUG(glEndQueryEXT(GL_ANY_SAMPLES_PASSED_EXT)); +#else + GLDEBUG(glEndQuery(GL_SAMPLES_PASSED)); +#endif + } +} + + +AABB KROctreeNode::getBounds() +{ + return m_bounds; +} + +void KROctreeNode::add(KRNode *pNode) +{ + int iChild = getChildIndex(pNode); + if(iChild == -1) { + m_sceneNodes.insert(pNode); + pNode->addToOctreeNode(this); + } else { + if(m_children[iChild] == NULL) { + m_children[iChild] = new KROctreeNode(this, getChildBounds(iChild)); + } + m_children[iChild]->add(pNode); + } +} + +AABB KROctreeNode::getChildBounds(int iChild) +{ + Vector3 center = m_bounds.center(); + + return AABB::Create( + Vector3::Create( + (iChild & 1) == 0 ? m_bounds.min.x : center.x, + (iChild & 2) == 0 ? m_bounds.min.y : center.y, + (iChild & 4) == 0 ? m_bounds.min.z : center.z), + Vector3::Create( + (iChild & 1) == 0 ? center.x : m_bounds.max.x, + (iChild & 2) == 0 ? center.y : m_bounds.max.y, + (iChild & 4) == 0 ? center.z : m_bounds.max.z) + ); +} + +int KROctreeNode::getChildIndex(KRNode *pNode) +{ + for(int iChild=0; iChild < 8; iChild++) { + if(getChildBounds(iChild).contains(pNode->getBounds())) { + return iChild; + } + } + return -1; +} + +void KROctreeNode::trim() +{ + for(int iChild = 0; iChild < 8; iChild++) { + if(m_children[iChild]) { + if(m_children[iChild]->isEmpty()) { + delete m_children[iChild]; + m_children[iChild] = NULL; + } + } + } +} + +void KROctreeNode::remove(KRNode *pNode) +{ + m_sceneNodes.erase(pNode); +} + +void KROctreeNode::update(KRNode *pNode) +{ + +} + +bool KROctreeNode::isEmpty() const +{ + for(int i=0; i<8; i++) { + if(m_children[i]) { + return false; + } + } + return m_sceneNodes.empty(); +} + +bool KROctreeNode::canShrinkRoot() const +{ + int cChildren = 0; + for(int i=0; i<8; i++) { + if(m_children[i]) { + cChildren++; + } + } + return cChildren <= 1 && m_sceneNodes.empty(); +} + +KROctreeNode *KROctreeNode::stripChild() +{ + // Return the first found child and update its reference to NULL so that the destructor will not free it. This is used for shrinking the octree + // NOTE: The caller of this function will be responsible for freeing the child object. It is also possible to return a NULL + for(int i=0; i<8; i++) { + if(m_children[i]) { + KROctreeNode *child = m_children[i]; + child->m_parent = NULL; + m_children[i] = NULL; + return child; + } + } + return NULL; +} + + +KROctreeNode *KROctreeNode::getParent() +{ + return m_parent; +} + +KROctreeNode **KROctreeNode::getChildren() +{ + return m_children; +} + +std::set &KROctreeNode::getSceneNodes() +{ + return m_sceneNodes; +} + + +bool KROctreeNode::lineCast(const Vector3 &v0, const Vector3 &v1, HitInfo &hitinfo, unsigned int layer_mask) +{ + bool hit_found = false; + if(hitinfo.didHit() && v1 != hitinfo.getPosition()) { + // Optimization: If we already have a hit, only search for hits that are closer + hit_found = lineCast(v0, hitinfo.getPosition(), hitinfo, layer_mask); + } else { + if(getBounds().intersectsLine(v0, v1)) { + for(std::set::iterator nodes_itr=m_sceneNodes.begin(); nodes_itr != m_sceneNodes.end(); nodes_itr++) { + KRCollider *collider = dynamic_cast(*nodes_itr); + if(collider) { + if(collider->lineCast(v0, v1, hitinfo, layer_mask)) hit_found = true; + } + } + + for(int i=0; i<8; i++) { + if(m_children[i]) { + if(m_children[i]->lineCast(v0, v1, hitinfo, layer_mask)) { + hit_found = true; + } + } + } + } + } + + return hit_found; +} + +bool KROctreeNode::rayCast(const Vector3 &v0, const Vector3 &dir, HitInfo &hitinfo, unsigned int layer_mask) +{ + bool hit_found = false; + if(hitinfo.didHit()) { + // Optimization: If we already have a hit, only search for hits that are closer + hit_found = lineCast(v0, hitinfo.getPosition(), hitinfo, layer_mask); // Note: This is purposefully lineCast as opposed to RayCast + } else { + if(getBounds().intersectsRay(v0, dir)) { + for(std::set::iterator nodes_itr=m_sceneNodes.begin(); nodes_itr != m_sceneNodes.end(); nodes_itr++) { + KRCollider *collider = dynamic_cast(*nodes_itr); + if(collider) { + if(collider->rayCast(v0, dir, hitinfo, layer_mask)) hit_found = true; + } + } + + for(int i=0; i<8; i++) { + if(m_children[i]) { + if(m_children[i]->rayCast(v0, dir, hitinfo, layer_mask)) { + hit_found = true; + } + } + } + } + } + + return hit_found; +} + +bool KROctreeNode::sphereCast(const Vector3 &v0, const Vector3 &v1, float radius, HitInfo &hitinfo, unsigned int layer_mask) +{ + bool hit_found = false; + /* + // FINDME, TODO - Adapt this optimization to work with sphereCasts + + if(hitinfo.didHit() && v1 != hitinfo.getPosition()) { + // Optimization: If we already have a hit, only search for hits that are closer + hit_found = sphereCast(v0, hitinfo.getPosition(), radius, hitinfo, layer_mask); + } else { + */ + + AABB swept_bounds = AABB::Create(Vector3::Create(KRMIN(v0.x, v1.x) - radius, KRMIN(v0.y, v1.y) - radius, KRMIN(v0.z, v1.z) - radius), Vector3::Create(KRMAX(v0.x, v1.x) + radius, KRMAX(v0.y, v1.y) + radius, KRMAX(v0.z, v1.z) + radius)); + // FINDME, TODO - Investigate AABB - swept sphere intersections or OBB - AABB intersections: "if(getBounds().intersectsSweptSphere(v0, v1, radius)) {" + if(getBounds().intersects(swept_bounds)) { + + for(std::set::iterator nodes_itr=m_sceneNodes.begin(); nodes_itr != m_sceneNodes.end(); nodes_itr++) { + KRCollider *collider = dynamic_cast(*nodes_itr); + if(collider) { + if(collider->sphereCast(v0, v1, radius, hitinfo, layer_mask)) hit_found = true; + } + } + + for(int i=0; i<8; i++) { + if(m_children[i]) { + if(m_children[i]->sphereCast(v0, v1, radius, hitinfo, layer_mask)) { + hit_found = true; + } + } + } + } + // } + + return hit_found; +} + diff --git a/kraken/KROctreeNode.h b/kraken/KROctreeNode.h index 06b7ac7..6facfa3 100755 --- a/kraken/KROctreeNode.h +++ b/kraken/KROctreeNode.h @@ -1,65 +1,65 @@ -// -// KROctreeNode.h -// KREngine -// -// Created by Kearwood Gilbert on 2012-08-29. -// Copyright (c) 2012 Kearwood Software. All rights reserved. -// - -#ifndef KROCTREENODE_H -#define KROCTREENODE_H - -#include "KREngine-common.h" -#include "public/hitinfo.h" - -class KRNode; - -class KROctreeNode { -public: - KROctreeNode(KROctreeNode *parent, const AABB &bounds); - KROctreeNode(KROctreeNode *parent, const AABB &bounds, int iChild, KROctreeNode *pChild); - ~KROctreeNode(); - - KROctreeNode **getChildren(); - std::set &getSceneNodes(); - - void add(KRNode *pNode); - void remove(KRNode *pNode); - void update(KRNode *pNode); - - AABB getBounds(); - - KROctreeNode *getParent(); - void setChildNode(int iChild, KROctreeNode *pChild); - int getChildIndex(KRNode *pNode); - AABB getChildBounds(int iChild); - void trim(); - bool isEmpty() const; - - bool canShrinkRoot() const; - KROctreeNode *stripChild(); - - void beginOcclusionQuery(); - void endOcclusionQuery(); - - - GLuint m_occlusionQuery; - bool m_occlusionTested; - bool m_activeQuery; - - bool lineCast(const Vector3 &v0, const Vector3 &v1, HitInfo &hitinfo, unsigned int layer_mask); - bool rayCast(const Vector3 &v0, const Vector3 &dir, HitInfo &hitinfo, unsigned int layer_mask); - bool sphereCast(const Vector3 &v0, const Vector3 &v1, float radius, HitInfo &hitinfo, unsigned int layer_mask); - -private: - - AABB m_bounds; - - KROctreeNode *m_parent; - KROctreeNode *m_children[8]; - - std::setm_sceneNodes; -}; - - -#endif /* defined(KROCTREENODE_H) */ +// +// KROctreeNode.h +// KREngine +// +// Created by Kearwood Gilbert on 2012-08-29. +// Copyright (c) 2012 Kearwood Software. All rights reserved. +// + +#ifndef KROCTREENODE_H +#define KROCTREENODE_H + +#include "KREngine-common.h" +#include "hitinfo.h" + +class KRNode; + +class KROctreeNode { +public: + KROctreeNode(KROctreeNode *parent, const AABB &bounds); + KROctreeNode(KROctreeNode *parent, const AABB &bounds, int iChild, KROctreeNode *pChild); + ~KROctreeNode(); + + KROctreeNode **getChildren(); + std::set &getSceneNodes(); + + void add(KRNode *pNode); + void remove(KRNode *pNode); + void update(KRNode *pNode); + + AABB getBounds(); + + KROctreeNode *getParent(); + void setChildNode(int iChild, KROctreeNode *pChild); + int getChildIndex(KRNode *pNode); + AABB getChildBounds(int iChild); + void trim(); + bool isEmpty() const; + + bool canShrinkRoot() const; + KROctreeNode *stripChild(); + + void beginOcclusionQuery(); + void endOcclusionQuery(); + + + GLuint m_occlusionQuery; + bool m_occlusionTested; + bool m_activeQuery; + + bool lineCast(const Vector3 &v0, const Vector3 &v1, HitInfo &hitinfo, unsigned int layer_mask); + bool rayCast(const Vector3 &v0, const Vector3 &dir, HitInfo &hitinfo, unsigned int layer_mask); + bool sphereCast(const Vector3 &v0, const Vector3 &v1, float radius, HitInfo &hitinfo, unsigned int layer_mask); + +private: + + AABB m_bounds; + + KROctreeNode *m_parent; + KROctreeNode *m_children[8]; + + std::setm_sceneNodes; +}; + + +#endif /* defined(KROCTREENODE_H) */ diff --git a/kraken/KRPointLight.cpp b/kraken/KRPointLight.cpp index 21399bf..b32e0a0 100755 --- a/kraken/KRPointLight.cpp +++ b/kraken/KRPointLight.cpp @@ -1,226 +1,226 @@ -// -// KRPointLight.cpp -// KREngine -// -// Created by Kearwood Gilbert on 12-04-05. -// Copyright (c) 2012 Kearwood Software. All rights reserved. -// - -#include "KREngine-common.h" - -#include "KRPointLight.h" -#include "KRCamera.h" -#include "KRContext.h" -#include "KRStockGeometry.h" - -KRPointLight::KRPointLight(KRScene &scene, std::string name) : KRLight(scene, name) -{ - m_sphereVertices = NULL; - m_cVertices = 0; -} - -KRPointLight::~KRPointLight() -{ - if(m_sphereVertices) { - delete m_sphereVertices; - m_cVertices = 0; - } -} - -std::string KRPointLight::getElementName() { - return "point_light"; -} - -AABB KRPointLight::getBounds() { - float influence_radius = m_decayStart - sqrt(m_intensity * 0.01f) / sqrt(KRLIGHT_MIN_INFLUENCE); - if(influence_radius < m_flareOcclusionSize) { - influence_radius = m_flareOcclusionSize; - } - return AABB(Vector3(-influence_radius), Vector3(influence_radius), getModelMatrix()); -} - -void KRPointLight::render(KRCamera *pCamera, std::vector &point_lights, std::vector &directional_lights, std::vector&spot_lights, const KRViewport &viewport, KRNode::RenderPass renderPass) -{ - if(m_lod_visible <= LOD_VISIBILITY_PRESTREAM) return; - - KRLight::render(pCamera, point_lights, directional_lights, spot_lights, viewport, renderPass); - - bool bVisualize = renderPass == KRNode::RENDER_PASS_FORWARD_TRANSPARENT && pCamera->settings.bShowDeferred; - - if(renderPass == KRNode::RENDER_PASS_DEFERRED_LIGHTS || bVisualize) { - // Lights are rendered on the second pass of the deferred renderer - - std::vector this_light; - this_light.push_back(this); - - Vector3 light_position = getLocalTranslation(); - - float influence_radius = m_decayStart - sqrt(m_intensity * 0.01f) / sqrt(KRLIGHT_MIN_INFLUENCE); - - Matrix4 sphereModelMatrix = Matrix4(); - sphereModelMatrix.scale(influence_radius); - sphereModelMatrix.translate(light_position.x, light_position.y, light_position.z); - - if(viewport.visible(getBounds())) { // Cull out any lights not within the view frustrum - - Vector3 view_light_position = Matrix4::Dot(viewport.getViewMatrix(), light_position); - - bool bInsideLight = view_light_position.sqrMagnitude() <= (influence_radius + pCamera->settings.getPerspectiveNearZ()) * (influence_radius + pCamera->settings.getPerspectiveNearZ()); - - KRShader *pShader = getContext().getShaderManager()->getShader(bVisualize ? "visualize_overlay" : (bInsideLight ? "light_point_inside" : "light_point"), pCamera, this_light, std::vector(), std::vector(), 0, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, renderPass); - if(getContext().getShaderManager()->selectShader(*pCamera, pShader, viewport, sphereModelMatrix, this_light, std::vector(), std::vector(), 0, renderPass, Vector3::Zero(), 0.0f, Vector4::Zero())) { - - - pShader->setUniform(KRShader::KRENGINE_UNIFORM_LIGHT_COLOR, m_color); - pShader->setUniform(KRShader::KRENGINE_UNIFORM_LIGHT_INTENSITY, m_intensity * 0.01f); - pShader->setUniform(KRShader::KRENGINE_UNIFORM_LIGHT_DECAY_START, getDecayStart()); - pShader->setUniform(KRShader::KRENGINE_UNIFORM_LIGHT_CUTOFF, KRLIGHT_MIN_INFLUENCE); - pShader->setUniform(KRShader::KRENGINE_UNIFORM_LIGHT_POSITION, light_position); - - - if(bVisualize) { - // Enable additive blending - GLDEBUG(glEnable(GL_BLEND)); - GLDEBUG(glBlendFunc(GL_ONE, GL_ONE)); - } - - // Disable z-buffer write - GLDEBUG(glDepthMask(GL_FALSE)); - - - if(bInsideLight) { - - // Disable z-buffer test - GLDEBUG(glDisable(GL_DEPTH_TEST)); - - // Render a full screen quad - m_pContext->getMeshManager()->bindVBO(&m_pContext->getMeshManager()->KRENGINE_VBO_DATA_2D_SQUARE_VERTICES, 1.0f); - GLDEBUG(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)); - } else { -#if GL_OES_vertex_array_object - GLDEBUG(glBindVertexArrayOES(0)); -#endif - m_pContext->getMeshManager()->configureAttribs(1 << KRMesh::KRENGINE_ATTRIB_VERTEX); - // Render sphere of light's influence - generateMesh(); - - // Enable z-buffer test - GLDEBUG(glEnable(GL_DEPTH_TEST)); - GLDEBUG(glDepthFunc(GL_LEQUAL)); - GLDEBUG(glDepthRangef(0.0, 1.0)); - - GLDEBUG(glVertexAttribPointer(KRMesh::KRENGINE_ATTRIB_VERTEX, 3, GL_FLOAT, 0, 0, m_sphereVertices)); - GLDEBUG(glDrawArrays(GL_TRIANGLES, 0, m_cVertices)); - } - } - if(bVisualize) { - // Enable alpha blending - GLDEBUG(glEnable(GL_BLEND)); - GLDEBUG(glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA)); - } - } - } -} - -void KRPointLight::generateMesh() { - // Create a triangular facet approximation to a sphere - // Based on algorithm from Paul Bourke: http://paulbourke.net/miscellaneous/sphere_cylinder/ - - int iterations = 3; - int facet_count = pow(4, iterations) * 8; - - if(m_cVertices != facet_count * 3) { - if(m_sphereVertices) { - free(m_sphereVertices); - m_sphereVertices = NULL; - } - - m_cVertices = facet_count * 3; - - - class Facet3 { - public: - Facet3() { - - } - ~Facet3() { - - } - Vector3 p1; - Vector3 p2; - Vector3 p3; - }; - - std::vector f = std::vector(facet_count); - - int i,it; - float a; - Vector3 p[6] = { - Vector3(0,0,1), - Vector3(0,0,-1), - Vector3(-1,-1,0), - Vector3(1,-1,0), - Vector3(1,1,0), - Vector3(-1,1,0) - }; - - Vector3 pa,pb,pc; - int nt = 0,ntold; - - /* Create the level 0 object */ - a = 1 / sqrt(2.0); - for (i=0;i<6;i++) { - p[i].x *= a; - p[i].y *= a; - } - f[0].p1 = p[0]; f[0].p2 = p[3]; f[0].p3 = p[4]; - f[1].p1 = p[0]; f[1].p2 = p[4]; f[1].p3 = p[5]; - f[2].p1 = p[0]; f[2].p2 = p[5]; f[2].p3 = p[2]; - f[3].p1 = p[0]; f[3].p2 = p[2]; f[3].p3 = p[3]; - f[4].p1 = p[1]; f[4].p2 = p[4]; f[4].p3 = p[3]; - f[5].p1 = p[1]; f[5].p2 = p[5]; f[5].p3 = p[4]; - f[6].p1 = p[1]; f[6].p2 = p[2]; f[6].p3 = p[5]; - f[7].p1 = p[1]; f[7].p2 = p[3]; f[7].p3 = p[2]; - nt = 8; - - /* Bisect each edge and move to the surface of a unit sphere */ - for (it=0;it &point_lights, std::vector &directional_lights, std::vector&spot_lights, const KRViewport &viewport, KRNode::RenderPass renderPass) +{ + if(m_lod_visible <= LOD_VISIBILITY_PRESTREAM) return; + + KRLight::render(pCamera, point_lights, directional_lights, spot_lights, viewport, renderPass); + + bool bVisualize = renderPass == KRNode::RENDER_PASS_FORWARD_TRANSPARENT && pCamera->settings.bShowDeferred; + + if(renderPass == KRNode::RENDER_PASS_DEFERRED_LIGHTS || bVisualize) { + // Lights are rendered on the second pass of the deferred renderer + + std::vector this_light; + this_light.push_back(this); + + Vector3 light_position = getLocalTranslation(); + + float influence_radius = m_decayStart - sqrt(m_intensity * 0.01f) / sqrt(KRLIGHT_MIN_INFLUENCE); + + Matrix4 sphereModelMatrix = Matrix4(); + sphereModelMatrix.scale(influence_radius); + sphereModelMatrix.translate(light_position.x, light_position.y, light_position.z); + + if(viewport.visible(getBounds())) { // Cull out any lights not within the view frustrum + + Vector3 view_light_position = Matrix4::Dot(viewport.getViewMatrix(), light_position); + + bool bInsideLight = view_light_position.sqrMagnitude() <= (influence_radius + pCamera->settings.getPerspectiveNearZ()) * (influence_radius + pCamera->settings.getPerspectiveNearZ()); + + KRShader *pShader = getContext().getShaderManager()->getShader(bVisualize ? "visualize_overlay" : (bInsideLight ? "light_point_inside" : "light_point"), pCamera, this_light, std::vector(), std::vector(), 0, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, renderPass); + if(getContext().getShaderManager()->selectShader(*pCamera, pShader, viewport, sphereModelMatrix, this_light, std::vector(), std::vector(), 0, renderPass, Vector3::Zero(), 0.0f, Vector4::Zero())) { + + + pShader->setUniform(KRShader::KRENGINE_UNIFORM_LIGHT_COLOR, m_color); + pShader->setUniform(KRShader::KRENGINE_UNIFORM_LIGHT_INTENSITY, m_intensity * 0.01f); + pShader->setUniform(KRShader::KRENGINE_UNIFORM_LIGHT_DECAY_START, getDecayStart()); + pShader->setUniform(KRShader::KRENGINE_UNIFORM_LIGHT_CUTOFF, KRLIGHT_MIN_INFLUENCE); + pShader->setUniform(KRShader::KRENGINE_UNIFORM_LIGHT_POSITION, light_position); + + + if(bVisualize) { + // Enable additive blending + GLDEBUG(glEnable(GL_BLEND)); + GLDEBUG(glBlendFunc(GL_ONE, GL_ONE)); + } + + // Disable z-buffer write + GLDEBUG(glDepthMask(GL_FALSE)); + + + if(bInsideLight) { + + // Disable z-buffer test + GLDEBUG(glDisable(GL_DEPTH_TEST)); + + // Render a full screen quad + m_pContext->getMeshManager()->bindVBO(&m_pContext->getMeshManager()->KRENGINE_VBO_DATA_2D_SQUARE_VERTICES, 1.0f); + GLDEBUG(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)); + } else { +#if GL_OES_vertex_array_object + GLDEBUG(glBindVertexArrayOES(0)); +#endif + m_pContext->getMeshManager()->configureAttribs(1 << KRMesh::KRENGINE_ATTRIB_VERTEX); + // Render sphere of light's influence + generateMesh(); + + // Enable z-buffer test + GLDEBUG(glEnable(GL_DEPTH_TEST)); + GLDEBUG(glDepthFunc(GL_LEQUAL)); + GLDEBUG(glDepthRangef(0.0, 1.0)); + + GLDEBUG(glVertexAttribPointer(KRMesh::KRENGINE_ATTRIB_VERTEX, 3, GL_FLOAT, 0, 0, m_sphereVertices)); + GLDEBUG(glDrawArrays(GL_TRIANGLES, 0, m_cVertices)); + } + } + if(bVisualize) { + // Enable alpha blending + GLDEBUG(glEnable(GL_BLEND)); + GLDEBUG(glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA)); + } + } + } +} + +void KRPointLight::generateMesh() { + // Create a triangular facet approximation to a sphere + // Based on algorithm from Paul Bourke: http://paulbourke.net/miscellaneous/sphere_cylinder/ + + int iterations = 3; + int facet_count = pow(4, iterations) * 8; + + if(m_cVertices != facet_count * 3) { + if(m_sphereVertices) { + free(m_sphereVertices); + m_sphereVertices = NULL; + } + + m_cVertices = facet_count * 3; + + + class Facet3 { + public: + Facet3() { + + } + ~Facet3() { + + } + Vector3 p1; + Vector3 p2; + Vector3 p3; + }; + + std::vector f = std::vector(facet_count); + + int i,it; + float a; + Vector3 p[6] = { + Vector3::Create(0,0,1), + Vector3::Create(0,0,-1), + Vector3::Create(-1,-1,0), + Vector3::Create(1,-1,0), + Vector3::Create(1,1,0), + Vector3::Create(-1,1,0) + }; + + Vector3 pa,pb,pc; + int nt = 0,ntold; + + /* Create the level 0 object */ + a = 1.0f / sqrtf(2.0f); + for (i=0;i<6;i++) { + p[i].x *= a; + p[i].y *= a; + } + f[0].p1 = p[0]; f[0].p2 = p[3]; f[0].p3 = p[4]; + f[1].p1 = p[0]; f[1].p2 = p[4]; f[1].p3 = p[5]; + f[2].p1 = p[0]; f[2].p2 = p[5]; f[2].p3 = p[2]; + f[3].p1 = p[0]; f[3].p2 = p[2]; f[3].p3 = p[3]; + f[4].p1 = p[1]; f[4].p2 = p[4]; f[4].p3 = p[3]; + f[5].p1 = p[1]; f[5].p2 = p[5]; f[5].p3 = p[4]; + f[6].p1 = p[1]; f[6].p2 = p[2]; f[6].p3 = p[5]; + f[7].p1 = p[1]; f[7].p2 = p[3]; f[7].p3 = p[2]; + nt = 8; + + /* Bisect each edge and move to the surface of a unit sphere */ + for (it=0;it KRResource::LoadObj(KRContext &context, const std::string& path) -{ - std::vector resources; - - KRMesh *new_mesh = new KRMesh(context, KRResource::GetFileBase(path)); - resources.push_back(new_mesh); - - KRMesh::mesh_info mi; - - std::vector material_names_t; - - KRDataBlock data; - - char szSymbol[500][256]; - - int *pFaces = NULL; - - vector m_materials; - - if(data.load(path)) { - // -----=====----- Get counts -----=====----- - - int cVertexData = 0; - - - int cFaces = 1; - int cMaterialFaceStart = 1; - - char *pScan = (char *)data.getStart(); - char *pEnd = (char *)data.getEnd(); - while(pScan < pEnd) { - - // Scan through whitespace - while(pScan < pEnd && (*pScan == ' ' || *pScan == '\t' || *pScan == '\r' || *pScan == '\n')) { - pScan++; - } - - if(*pScan == '#') { - // Line is a comment line - - // Scan to the end of the line - while(pScan < pEnd && *pScan != '\r' && *pScan != '\n') { - pScan++; - } - } else { - int cSymbols = 0; - while(pScan < pEnd && *pScan != '\n' && *pScan != '\r') { - - char *pDest = szSymbol[cSymbols++]; - while(pScan < pEnd && *pScan != ' ' && *pScan != '\n' && *pScan != '\r') { - *pDest++ = *pScan++; - } - *pDest = '\0'; - - // Scan through whitespace, but don't advance to next line - while(pScan < pEnd && (*pScan == ' ' || *pScan == '\t')) { - pScan++; - } - } - - if(strcmp(szSymbol[0], "v") == 0) { - // Vertex (v) - } else if(strcmp(szSymbol[0], "vt") == 0) { - // Vertex Texture UV Coordinate (vt) - } else if(strcmp(szSymbol[0], "vn") == 0) { - // Vertex Normal (vn) - } else if(strcmp(szSymbol[0], "f") == 0) { - // Face (f) - int cFaceVertexes = (cSymbols - 3) * 3; // 3 vertexes per triangle. Triangles have 4 symbols. Quads have 5 symbols and generate two triangles. - cVertexData += cFaceVertexes; - cFaces += cFaceVertexes * 3 + 1; // Allocate space for count of vertices, Vertex Index, Texture Coordinate Index, and Normal Index - - } else if(strcmp(szSymbol[0], "usemtl") == 0) { - // Use Material (usemtl) - if(cMaterialFaceStart - cFaces > 0) { - cFaces++; - - } - material_names_t.push_back(std::string(szSymbol[1])); - } - - } - } - - - // -----=====----- Populate vertexes and faces -----=====----- - - int *pFaces = (int *)malloc(sizeof(int) * (cFaces + 1)); - assert(pFaces != NULL); - - std::vector indexed_vertices; - std::vector indexed_uva; - std::vector indexed_normals; - - int *pFace = pFaces; - int *pMaterialFaces = pFace++; - *pMaterialFaces = 0; - - - - - // -------- - - pScan = (char *)data.getStart(); - while(pScan < pEnd) { - - // Scan through whitespace - while(pScan < pEnd && (*pScan == ' ' || *pScan == '\t' || *pScan == '\r' || *pScan == '\n')) { - pScan++; - } - - if(*pScan == '#') { - // Line is a comment line - - // Scan to the end of the line - while(pScan < pEnd && *pScan != '\r' && *pScan != '\n') { - pScan++; - } - } else { - int cSymbols = 0; - while(pScan < pEnd && *pScan != '\n' && *pScan != '\r') { - - char *pDest = szSymbol[cSymbols++]; - while(pScan < pEnd && *pScan != ' ' && *pScan != '\n' && *pScan != '\r') { - *pDest++ = *pScan++; - } - *pDest = '\0'; - - // Scan through whitespace, but don't advance to next line - while(pScan < pEnd && (*pScan == ' ' || *pScan == '\t')) { - pScan++; - } - } - - if(strcmp(szSymbol[0], "v") == 0) { - // Vertex (v) - float x, y, z; - char *pChar = szSymbol[1]; - x = strtof(pChar, &pChar); - pChar = szSymbol[2]; - y = strtof(pChar, &pChar); - pChar = szSymbol[3]; - z = strtof(pChar, &pChar); - indexed_vertices.push_back(Vector3(x,y,z)); - } else if(strcmp(szSymbol[0], "vt") == 0) { - // Vertex Texture UV Coordinate (vt) - char *pChar = szSymbol[1]; - float u,v; - - u = strtof(pChar, &pChar); - pChar = szSymbol[2]; - v = strtof(pChar, &pChar); - indexed_uva.push_back(Vector2(u,v)); - } else if(strcmp(szSymbol[0], "vn") == 0) { - // Vertex Normal (vn) - float x,y,z; - char *pChar = szSymbol[1]; - x = strtof(pChar, &pChar); - pChar = szSymbol[2]; - y = strtof(pChar, &pChar); - pChar = szSymbol[3]; - z = strtof(pChar, &pChar); - indexed_normals.push_back(Vector3(x,y,z)); - } else if(strcmp(szSymbol[0], "f") == 0) { - // Face (f) - int cFaceVertices = cSymbols - 1; - - *pFace++ = cFaceVertices; - for(int iSymbol=1; iSymbol < cSymbols; iSymbol++) { - char *pChar = szSymbol[iSymbol]; - if(*pChar == '.' || (*pChar >= '0' && *pChar <= '9')) { - *pFace++ = strtol(pChar, &pChar, 10) - 1; // Vertex Index - - if(*pChar == '/') { - pChar++; - if(*pChar == '/') { - *pFace++ = -1; - } else { - *pFace++ = strtol(pChar, &pChar, 10) - 1; // Texture Coordinate Index - } - } else { - *pFace++ = -1; - } - - if(*pChar == '/') { - pChar++; - if(*pChar == '/') { - *pFace++ = -1; - } else { - *pFace++ = strtol(pChar, &pChar, 10) - 1; // Normal Index - } - } else { - *pFace++ = -1; - } - while(*pChar == '/') { - pChar++; - strtol(pChar, &pChar, 10); - } - } - } - - - } else if(strcmp(szSymbol[0], "usemtl") == 0) { - // Use Material (usemtl) - if(pFace - pMaterialFaces > 1) { - *pMaterialFaces = pFace - pMaterialFaces - 1; - pMaterialFaces = pFace++; - } - } - } - } - - - *pMaterialFaces = pFace - pMaterialFaces - 1; - *pFace++ = 0; - - - int iVertex = 0; - - - std::vector::iterator material_itr = material_names_t.begin(); - KRMesh::pack_material *pMaterial = new KRMesh::pack_material(); - pMaterial->start_vertex = iVertex; - pMaterial->vertex_count = 0; - memset(pMaterial->szName, 256, 0); - if(material_itr < material_names_t.end()) { - strncpy(pMaterial->szName, (*material_itr++).c_str(), 256); - } - m_materials.push_back(pMaterial); - - - pFace = pFaces; - while(*pFace != 0 && iVertex < cVertexData) { - pMaterial->start_vertex = iVertex; - - int *pMaterialEndFace = pFace + *pFace; - ++pFace; - while(pFace < pMaterialEndFace && iVertex < cVertexData) { - int cFaceVertexes = *pFace; - Vector3 firstFaceVertex; - Vector3 prevFaceVertex; - Vector3 firstFaceNormal; - Vector3 prevFaceNormal; - Vector2 firstFaceUva; - Vector2 prevFaceUva; - for(int iFaceVertex=0; iFaceVertex < cFaceVertexes; iFaceVertex++) { - if(iFaceVertex > 2) { - // There have already been 3 vertices. Now we need to split the quad into a second triangle composed of the 1st, 3rd, and 4th vertices - iVertex+=2; - - mi.vertices.push_back(firstFaceVertex); - mi.uva.push_back(firstFaceUva); - mi.normals.push_back(firstFaceNormal); - - mi.vertices.push_back(prevFaceVertex); - mi.uva.push_back(prevFaceUva); - mi.normals.push_back(prevFaceNormal); - } - Vector3 vertex = indexed_vertices[pFace[iFaceVertex*3+1]]; - Vector2 new_uva; - if(pFace[iFaceVertex*3+2] >= 0) { - new_uva = indexed_uva[pFace[iFaceVertex*3+2]]; - } - Vector3 normal; - if(pFace[iFaceVertex*3+3] >= 0){ - Vector3 normal = indexed_normals[pFace[iFaceVertex*3+3]]; - } - - mi.vertices.push_back(vertex); - mi.uva.push_back(new_uva); - mi.normals.push_back(normal); - - if(iFaceVertex==0) { - firstFaceVertex = vertex; - firstFaceUva = new_uva; - firstFaceNormal = normal; - } - prevFaceVertex = vertex; - prevFaceUva = new_uva; - prevFaceNormal = normal; - - iVertex++; - } - pFace += cFaceVertexes * 3 + 1; - } - pMaterial->vertex_count = iVertex - pMaterial->start_vertex; - if(*pFace != 0) { - pMaterial = new KRMesh::pack_material(); - pMaterial->start_vertex = iVertex; - pMaterial->vertex_count = 0; - memset(pMaterial->szName, 256, 0); - - if(material_itr < material_names_t.end()) { - strncpy(pMaterial->szName, (*material_itr++).c_str(), 256); - } - m_materials.push_back(pMaterial); - } - } - - for(int iMaterial=0; iMaterial < m_materials.size(); iMaterial++) { - KRMesh::pack_material *pNewMaterial = m_materials[iMaterial]; - if(pNewMaterial->vertex_count > 0) { - mi.material_names.push_back(std::string(pNewMaterial->szName)); - mi.submesh_starts.push_back(pNewMaterial->start_vertex); - mi.submesh_lengths.push_back(pNewMaterial->vertex_count); - } - delete pNewMaterial; - } - - // TODO: Bones not yet supported for OBJ -// std::vector bone_names; -// std::vector bone_bind_poses; -// std::vector > bone_indexes; -// std::vector > bone_weights; -// -// std::vector<__uint16_t> vertex_indexes; -// std::vector > vertex_index_bases; - - mi.format = KRMesh::KRENGINE_MODEL_FORMAT_TRIANGLES; - new_mesh->LoadData(mi, true, false); - } - - if(pFaces) { - free(pFaces); - } - - return resources; -} +// +// KRResource_obj.cpp +// KREngine +// +// Created by Kearwood Gilbert on 12-03-22. +// Copyright (c) 2012 Kearwood Software. All rights reserved. +// + +#include "KREngine-common.h" + +#include "KRResource.h" +#include "KRMesh.h" + +std::vector KRResource::LoadObj(KRContext &context, const std::string& path) +{ + std::vector resources; + + KRMesh *new_mesh = new KRMesh(context, KRResource::GetFileBase(path)); + resources.push_back(new_mesh); + + KRMesh::mesh_info mi; + + std::vector material_names_t; + + KRDataBlock data; + + char szSymbol[500][256]; + + int *pFaces = NULL; + + vector m_materials; + + if(data.load(path)) { + // -----=====----- Get counts -----=====----- + + int cVertexData = 0; + + + int cFaces = 1; + int cMaterialFaceStart = 1; + + char *pScan = (char *)data.getStart(); + char *pEnd = (char *)data.getEnd(); + while(pScan < pEnd) { + + // Scan through whitespace + while(pScan < pEnd && (*pScan == ' ' || *pScan == '\t' || *pScan == '\r' || *pScan == '\n')) { + pScan++; + } + + if(*pScan == '#') { + // Line is a comment line + + // Scan to the end of the line + while(pScan < pEnd && *pScan != '\r' && *pScan != '\n') { + pScan++; + } + } else { + int cSymbols = 0; + while(pScan < pEnd && *pScan != '\n' && *pScan != '\r') { + + char *pDest = szSymbol[cSymbols++]; + while(pScan < pEnd && *pScan != ' ' && *pScan != '\n' && *pScan != '\r') { + *pDest++ = *pScan++; + } + *pDest = '\0'; + + // Scan through whitespace, but don't advance to next line + while(pScan < pEnd && (*pScan == ' ' || *pScan == '\t')) { + pScan++; + } + } + + if(strcmp(szSymbol[0], "v") == 0) { + // Vertex (v) + } else if(strcmp(szSymbol[0], "vt") == 0) { + // Vertex Texture UV Coordinate (vt) + } else if(strcmp(szSymbol[0], "vn") == 0) { + // Vertex Normal (vn) + } else if(strcmp(szSymbol[0], "f") == 0) { + // Face (f) + int cFaceVertexes = (cSymbols - 3) * 3; // 3 vertexes per triangle. Triangles have 4 symbols. Quads have 5 symbols and generate two triangles. + cVertexData += cFaceVertexes; + cFaces += cFaceVertexes * 3 + 1; // Allocate space for count of vertices, Vertex Index, Texture Coordinate Index, and Normal Index + + } else if(strcmp(szSymbol[0], "usemtl") == 0) { + // Use Material (usemtl) + if(cMaterialFaceStart - cFaces > 0) { + cFaces++; + + } + material_names_t.push_back(std::string(szSymbol[1])); + } + + } + } + + + // -----=====----- Populate vertexes and faces -----=====----- + + int *pFaces = (int *)malloc(sizeof(int) * (cFaces + 1)); + assert(pFaces != NULL); + + std::vector indexed_vertices; + std::vector indexed_uva; + std::vector indexed_normals; + + int *pFace = pFaces; + int *pMaterialFaces = pFace++; + *pMaterialFaces = 0; + + + + + // -------- + + pScan = (char *)data.getStart(); + while(pScan < pEnd) { + + // Scan through whitespace + while(pScan < pEnd && (*pScan == ' ' || *pScan == '\t' || *pScan == '\r' || *pScan == '\n')) { + pScan++; + } + + if(*pScan == '#') { + // Line is a comment line + + // Scan to the end of the line + while(pScan < pEnd && *pScan != '\r' && *pScan != '\n') { + pScan++; + } + } else { + int cSymbols = 0; + while(pScan < pEnd && *pScan != '\n' && *pScan != '\r') { + + char *pDest = szSymbol[cSymbols++]; + while(pScan < pEnd && *pScan != ' ' && *pScan != '\n' && *pScan != '\r') { + *pDest++ = *pScan++; + } + *pDest = '\0'; + + // Scan through whitespace, but don't advance to next line + while(pScan < pEnd && (*pScan == ' ' || *pScan == '\t')) { + pScan++; + } + } + + if(strcmp(szSymbol[0], "v") == 0) { + // Vertex (v) + float x, y, z; + char *pChar = szSymbol[1]; + x = strtof(pChar, &pChar); + pChar = szSymbol[2]; + y = strtof(pChar, &pChar); + pChar = szSymbol[3]; + z = strtof(pChar, &pChar); + indexed_vertices.push_back(Vector3::Create(x,y,z)); + } else if(strcmp(szSymbol[0], "vt") == 0) { + // Vertex Texture UV Coordinate (vt) + char *pChar = szSymbol[1]; + float u,v; + + u = strtof(pChar, &pChar); + pChar = szSymbol[2]; + v = strtof(pChar, &pChar); + indexed_uva.push_back(Vector2::Create(u,v)); + } else if(strcmp(szSymbol[0], "vn") == 0) { + // Vertex Normal (vn) + float x,y,z; + char *pChar = szSymbol[1]; + x = strtof(pChar, &pChar); + pChar = szSymbol[2]; + y = strtof(pChar, &pChar); + pChar = szSymbol[3]; + z = strtof(pChar, &pChar); + indexed_normals.push_back(Vector3::Create(x,y,z)); + } else if(strcmp(szSymbol[0], "f") == 0) { + // Face (f) + int cFaceVertices = cSymbols - 1; + + *pFace++ = cFaceVertices; + for(int iSymbol=1; iSymbol < cSymbols; iSymbol++) { + char *pChar = szSymbol[iSymbol]; + if(*pChar == '.' || (*pChar >= '0' && *pChar <= '9')) { + *pFace++ = strtol(pChar, &pChar, 10) - 1; // Vertex Index + + if(*pChar == '/') { + pChar++; + if(*pChar == '/') { + *pFace++ = -1; + } else { + *pFace++ = strtol(pChar, &pChar, 10) - 1; // Texture Coordinate Index + } + } else { + *pFace++ = -1; + } + + if(*pChar == '/') { + pChar++; + if(*pChar == '/') { + *pFace++ = -1; + } else { + *pFace++ = strtol(pChar, &pChar, 10) - 1; // Normal Index + } + } else { + *pFace++ = -1; + } + while(*pChar == '/') { + pChar++; + strtol(pChar, &pChar, 10); + } + } + } + + + } else if(strcmp(szSymbol[0], "usemtl") == 0) { + // Use Material (usemtl) + if(pFace - pMaterialFaces > 1) { + *pMaterialFaces = pFace - pMaterialFaces - 1; + pMaterialFaces = pFace++; + } + } + } + } + + + *pMaterialFaces = pFace - pMaterialFaces - 1; + *pFace++ = 0; + + + int iVertex = 0; + + + std::vector::iterator material_itr = material_names_t.begin(); + KRMesh::pack_material *pMaterial = new KRMesh::pack_material(); + pMaterial->start_vertex = iVertex; + pMaterial->vertex_count = 0; + memset(pMaterial->szName, 256, 0); + if(material_itr < material_names_t.end()) { + strncpy(pMaterial->szName, (*material_itr++).c_str(), 256); + } + m_materials.push_back(pMaterial); + + + pFace = pFaces; + while(*pFace != 0 && iVertex < cVertexData) { + pMaterial->start_vertex = iVertex; + + int *pMaterialEndFace = pFace + *pFace; + ++pFace; + while(pFace < pMaterialEndFace && iVertex < cVertexData) { + int cFaceVertexes = *pFace; + Vector3 firstFaceVertex; + Vector3 prevFaceVertex; + Vector3 firstFaceNormal; + Vector3 prevFaceNormal; + Vector2 firstFaceUva; + Vector2 prevFaceUva; + for(int iFaceVertex=0; iFaceVertex < cFaceVertexes; iFaceVertex++) { + if(iFaceVertex > 2) { + // There have already been 3 vertices. Now we need to split the quad into a second triangle composed of the 1st, 3rd, and 4th vertices + iVertex+=2; + + mi.vertices.push_back(firstFaceVertex); + mi.uva.push_back(firstFaceUva); + mi.normals.push_back(firstFaceNormal); + + mi.vertices.push_back(prevFaceVertex); + mi.uva.push_back(prevFaceUva); + mi.normals.push_back(prevFaceNormal); + } + Vector3 vertex = indexed_vertices[pFace[iFaceVertex*3+1]]; + Vector2 new_uva; + if(pFace[iFaceVertex*3+2] >= 0) { + new_uva = indexed_uva[pFace[iFaceVertex*3+2]]; + } + Vector3 normal; + if(pFace[iFaceVertex*3+3] >= 0){ + Vector3 normal = indexed_normals[pFace[iFaceVertex*3+3]]; + } + + mi.vertices.push_back(vertex); + mi.uva.push_back(new_uva); + mi.normals.push_back(normal); + + if(iFaceVertex==0) { + firstFaceVertex = vertex; + firstFaceUva = new_uva; + firstFaceNormal = normal; + } + prevFaceVertex = vertex; + prevFaceUva = new_uva; + prevFaceNormal = normal; + + iVertex++; + } + pFace += cFaceVertexes * 3 + 1; + } + pMaterial->vertex_count = iVertex - pMaterial->start_vertex; + if(*pFace != 0) { + pMaterial = new KRMesh::pack_material(); + pMaterial->start_vertex = iVertex; + pMaterial->vertex_count = 0; + memset(pMaterial->szName, 256, 0); + + if(material_itr < material_names_t.end()) { + strncpy(pMaterial->szName, (*material_itr++).c_str(), 256); + } + m_materials.push_back(pMaterial); + } + } + + for(int iMaterial=0; iMaterial < m_materials.size(); iMaterial++) { + KRMesh::pack_material *pNewMaterial = m_materials[iMaterial]; + if(pNewMaterial->vertex_count > 0) { + mi.material_names.push_back(std::string(pNewMaterial->szName)); + mi.submesh_starts.push_back(pNewMaterial->start_vertex); + mi.submesh_lengths.push_back(pNewMaterial->vertex_count); + } + delete pNewMaterial; + } + + // TODO: Bones not yet supported for OBJ +// std::vector bone_names; +// std::vector bone_bind_poses; +// std::vector > bone_indexes; +// std::vector > bone_weights; +// +// std::vector<__uint16_t> vertex_indexes; +// std::vector > vertex_index_bases; + + mi.format = KRMesh::KRENGINE_MODEL_FORMAT_TRIANGLES; + new_mesh->LoadData(mi, true, false); + } + + if(pFaces) { + free(pFaces); + } + + return resources; +} diff --git a/kraken/KRReverbZone.cpp b/kraken/KRReverbZone.cpp index 144d35a..752198e 100755 --- a/kraken/KRReverbZone.cpp +++ b/kraken/KRReverbZone.cpp @@ -1,166 +1,166 @@ -// -// KRReverbZone.cpp -// KREngine -// -// Created by Kearwood Gilbert on 2012-12-06. -// Copyright (c) 2012 Kearwood Software. All rights reserved. -// - -#include "KRReverbZone.h" -#include "KRContext.h" - -KRReverbZone::KRReverbZone(KRScene &scene, std::string name) : KRNode(scene, name) -{ - m_reverb = ""; - m_reverb_gain = 1.0f; - m_gradient_distance = 0.25f; - -} - -KRReverbZone::~KRReverbZone() -{ -} - -std::string KRReverbZone::getElementName() { - return "reverb_zone"; -} - -tinyxml2::XMLElement *KRReverbZone::saveXML( tinyxml2::XMLNode *parent) -{ - tinyxml2::XMLElement *e = KRNode::saveXML(parent); - e->SetAttribute("zone", m_zone.c_str()); - e->SetAttribute("sample", m_reverb.c_str()); - e->SetAttribute("gain", m_reverb_gain); - e->SetAttribute("gradient", m_gradient_distance); - return e; -} - -void KRReverbZone::loadXML(tinyxml2::XMLElement *e) -{ - KRNode::loadXML(e); - - m_zone = e->Attribute("zone"); - - m_gradient_distance = 0.25f; - if(e->QueryFloatAttribute("gradient", &m_gradient_distance) != tinyxml2::XML_SUCCESS) { - m_gradient_distance = 0.25f; - } - - m_reverb = e->Attribute("sample"); - - m_reverb_gain = 1.0f; - if(e->QueryFloatAttribute("gain", &m_reverb_gain) != tinyxml2::XML_SUCCESS) { - m_reverb_gain = 1.0f; - } -} - -std::string KRReverbZone::getReverb() -{ - return m_reverb; -} - -void KRReverbZone::setReverb(const std::string &reverb) -{ - m_reverb = reverb; -} - -float KRReverbZone::getReverbGain() -{ - return m_reverb_gain; -} - -void KRReverbZone::setReverbGain(float reverb_gain) -{ - m_reverb_gain = reverb_gain; -} - -std::string KRReverbZone::getZone() -{ - return m_zone; -} - -void KRReverbZone::setZone(const std::string &zone) -{ - m_zone = zone; -} - -void KRReverbZone::render(KRCamera *pCamera, std::vector &point_lights, std::vector &directional_lights, std::vector&spot_lights, const KRViewport &viewport, KRNode::RenderPass renderPass) -{ - if(m_lod_visible <= LOD_VISIBILITY_PRESTREAM) return; - - KRNode::render(pCamera, point_lights, directional_lights, spot_lights, viewport, renderPass); - - bool bVisualize = pCamera->settings.debug_display == KRRenderSettings::KRENGINE_DEBUG_DISPLAY_SIREN_REVERB_ZONES; - - if(renderPass == KRNode::RENDER_PASS_FORWARD_TRANSPARENT && bVisualize) { - Matrix4 sphereModelMatrix = getModelMatrix(); - - KRShader *pShader = getContext().getShaderManager()->getShader("visualize_overlay", pCamera, point_lights, directional_lights, spot_lights, 0, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, renderPass); - - if(getContext().getShaderManager()->selectShader(*pCamera, pShader, viewport, sphereModelMatrix, point_lights, directional_lights, spot_lights, 0, renderPass, Vector3::Zero(), 0.0f, Vector4::Zero())) { - - // Enable additive blending - GLDEBUG(glEnable(GL_BLEND)); - GLDEBUG(glBlendFunc(GL_ONE, GL_ONE)); - - - // Disable z-buffer write - GLDEBUG(glDepthMask(GL_FALSE)); - - // Enable z-buffer test - GLDEBUG(glEnable(GL_DEPTH_TEST)); - GLDEBUG(glDepthFunc(GL_LEQUAL)); - GLDEBUG(glDepthRangef(0.0, 1.0)); - std::vector sphereModels = getContext().getMeshManager()->getModel("__sphere"); - if(sphereModels.size()) { - for(int i=0; i < sphereModels[0]->getSubmeshCount(); i++) { - sphereModels[0]->renderSubmesh(i, renderPass, getName(), "visualize_overlay", 1.0f); - } - } - - // Enable alpha blending - GLDEBUG(glEnable(GL_BLEND)); - GLDEBUG(glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA)); - } - } -} - - -float KRReverbZone::getGradientDistance() -{ - return m_gradient_distance; -} - -void KRReverbZone::setGradientDistance(float gradient_distance) -{ - m_gradient_distance = gradient_distance; -} - -AABB KRReverbZone::getBounds() { - // Reverb zones always have a -1, -1, -1 to 1, 1, 1 bounding box - return AABB(-Vector3::One(), Vector3::One(), getModelMatrix()); -} - -float KRReverbZone::getContainment(const Vector3 &pos) -{ - AABB bounds = getBounds(); - if(bounds.contains(pos)) { - Vector3 size = bounds.size(); - Vector3 diff = pos - bounds.center(); - diff = diff * 2.0f; - diff = Vector3(diff.x / size.x, diff.y / size.y, diff.z / size.z); - float d = diff.magnitude(); - - if(m_gradient_distance <= 0.0f) { - // Avoid division by zero - d = d > 1.0f ? 0.0f : 1.0f; - } else { - d = (1.0f - d) / m_gradient_distance; - d = KRCLAMP(d, 0.0f, 1.0f); - } - return d; - - } else { - return 0.0f; - } +// +// KRReverbZone.cpp +// KREngine +// +// Created by Kearwood Gilbert on 2012-12-06. +// Copyright (c) 2012 Kearwood Software. All rights reserved. +// + +#include "KRReverbZone.h" +#include "KRContext.h" + +KRReverbZone::KRReverbZone(KRScene &scene, std::string name) : KRNode(scene, name) +{ + m_reverb = ""; + m_reverb_gain = 1.0f; + m_gradient_distance = 0.25f; + +} + +KRReverbZone::~KRReverbZone() +{ +} + +std::string KRReverbZone::getElementName() { + return "reverb_zone"; +} + +tinyxml2::XMLElement *KRReverbZone::saveXML( tinyxml2::XMLNode *parent) +{ + tinyxml2::XMLElement *e = KRNode::saveXML(parent); + e->SetAttribute("zone", m_zone.c_str()); + e->SetAttribute("sample", m_reverb.c_str()); + e->SetAttribute("gain", m_reverb_gain); + e->SetAttribute("gradient", m_gradient_distance); + return e; +} + +void KRReverbZone::loadXML(tinyxml2::XMLElement *e) +{ + KRNode::loadXML(e); + + m_zone = e->Attribute("zone"); + + m_gradient_distance = 0.25f; + if(e->QueryFloatAttribute("gradient", &m_gradient_distance) != tinyxml2::XML_SUCCESS) { + m_gradient_distance = 0.25f; + } + + m_reverb = e->Attribute("sample"); + + m_reverb_gain = 1.0f; + if(e->QueryFloatAttribute("gain", &m_reverb_gain) != tinyxml2::XML_SUCCESS) { + m_reverb_gain = 1.0f; + } +} + +std::string KRReverbZone::getReverb() +{ + return m_reverb; +} + +void KRReverbZone::setReverb(const std::string &reverb) +{ + m_reverb = reverb; +} + +float KRReverbZone::getReverbGain() +{ + return m_reverb_gain; +} + +void KRReverbZone::setReverbGain(float reverb_gain) +{ + m_reverb_gain = reverb_gain; +} + +std::string KRReverbZone::getZone() +{ + return m_zone; +} + +void KRReverbZone::setZone(const std::string &zone) +{ + m_zone = zone; +} + +void KRReverbZone::render(KRCamera *pCamera, std::vector &point_lights, std::vector &directional_lights, std::vector&spot_lights, const KRViewport &viewport, KRNode::RenderPass renderPass) +{ + if(m_lod_visible <= LOD_VISIBILITY_PRESTREAM) return; + + KRNode::render(pCamera, point_lights, directional_lights, spot_lights, viewport, renderPass); + + bool bVisualize = pCamera->settings.debug_display == KRRenderSettings::KRENGINE_DEBUG_DISPLAY_SIREN_REVERB_ZONES; + + if(renderPass == KRNode::RENDER_PASS_FORWARD_TRANSPARENT && bVisualize) { + Matrix4 sphereModelMatrix = getModelMatrix(); + + KRShader *pShader = getContext().getShaderManager()->getShader("visualize_overlay", pCamera, point_lights, directional_lights, spot_lights, 0, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, renderPass); + + if(getContext().getShaderManager()->selectShader(*pCamera, pShader, viewport, sphereModelMatrix, point_lights, directional_lights, spot_lights, 0, renderPass, Vector3::Zero(), 0.0f, Vector4::Zero())) { + + // Enable additive blending + GLDEBUG(glEnable(GL_BLEND)); + GLDEBUG(glBlendFunc(GL_ONE, GL_ONE)); + + + // Disable z-buffer write + GLDEBUG(glDepthMask(GL_FALSE)); + + // Enable z-buffer test + GLDEBUG(glEnable(GL_DEPTH_TEST)); + GLDEBUG(glDepthFunc(GL_LEQUAL)); + GLDEBUG(glDepthRangef(0.0, 1.0)); + std::vector sphereModels = getContext().getMeshManager()->getModel("__sphere"); + if(sphereModels.size()) { + for(int i=0; i < sphereModels[0]->getSubmeshCount(); i++) { + sphereModels[0]->renderSubmesh(i, renderPass, getName(), "visualize_overlay", 1.0f); + } + } + + // Enable alpha blending + GLDEBUG(glEnable(GL_BLEND)); + GLDEBUG(glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA)); + } + } +} + + +float KRReverbZone::getGradientDistance() +{ + return m_gradient_distance; +} + +void KRReverbZone::setGradientDistance(float gradient_distance) +{ + m_gradient_distance = gradient_distance; +} + +AABB KRReverbZone::getBounds() { + // Reverb zones always have a -1, -1, -1 to 1, 1, 1 bounding box + return AABB::Create(-Vector3::One(), Vector3::One(), getModelMatrix()); +} + +float KRReverbZone::getContainment(const Vector3 &pos) +{ + AABB bounds = getBounds(); + if(bounds.contains(pos)) { + Vector3 size = bounds.size(); + Vector3 diff = pos - bounds.center(); + diff = diff * 2.0f; + diff = Vector3::Create(diff.x / size.x, diff.y / size.y, diff.z / size.z); + float d = diff.magnitude(); + + if(m_gradient_distance <= 0.0f) { + // Avoid division by zero + d = d > 1.0f ? 0.0f : 1.0f; + } else { + d = (1.0f - d) / m_gradient_distance; + d = KRCLAMP(d, 0.0f, 1.0f); + } + return d; + + } else { + return 0.0f; + } } \ No newline at end of file diff --git a/kraken/KRScene.cpp b/kraken/KRScene.cpp index 0d9c37c..de0a771 100755 --- a/kraken/KRScene.cpp +++ b/kraken/KRScene.cpp @@ -1,608 +1,608 @@ -// -// KRScene.cpp -// KREngine -// -// Copyright 2012 Kearwood Gilbert. All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, are -// permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of -// conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list -// of conditions and the following disclaimer in the documentation and/or other materials -// provided with the distribution. -// -// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED -// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR -// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// The views and conclusions contained in the software and documentation are those of the -// authors and should not be interpreted as representing official policies, either expressed -// or implied, of Kearwood Gilbert. -// - -#include "KREngine-common.h" - -#include "KRLight.h" - -#include "KRScene.h" -#include "KRNode.h" -#include "KRStockGeometry.h" -#include "KRDirectionalLight.h" -#include "KRSpotLight.h" -#include "KRPointLight.h" -#include "KRAudioManager.h" - -const long KRENGINE_OCCLUSION_TEST_EXPIRY = 10; - -KRScene::KRScene(KRContext &context, std::string name) : KRResource(context, name) { - m_pFirstLight = NULL; - m_pRootNode = new KRNode(*this, "scene_root"); - notify_sceneGraphCreate(m_pRootNode); -} - -KRScene::~KRScene() { - delete m_pRootNode; - m_pRootNode = NULL; -} - -void KRScene::renderFrame(GLint defaultFBO, float deltaTime, int width, int height) { - getContext().startFrame(deltaTime); - KRCamera *camera = find("default_camera"); - if(camera == NULL) { - // Add a default camera if none are present - camera = new KRCamera(*this, "default_camera"); - m_pRootNode->addChild(camera); - } - - // FINDME - This should be moved to de-couple Siren from the Rendering pipeline - getContext().getAudioManager()->setEnableAudio(camera->settings.siren_enable); - getContext().getAudioManager()->setEnableHRTF(camera->settings.siren_enable_hrtf); - getContext().getAudioManager()->setEnableReverb(camera->settings.siren_enable_reverb); - getContext().getAudioManager()->setReverbMaxLength(camera->settings.siren_reverb_max_length); - getContext().getTextureManager()->setMaxAnisotropy(camera->settings.max_anisotropy); - - camera->renderFrame(defaultFBO, width, height); - getContext().endFrame(deltaTime); - physicsUpdate(deltaTime); -} - -std::set &KRScene::getAmbientZones() -{ - // FINDME, TODO - To support large scenes with many reverb / ambient zones, this function should take a KRAABB and cull out any far away zones - return m_ambientZoneNodes; -} - -std::set &KRScene::getReverbZones() -{ - // FINDME, TODO - To support large scenes with many reverb / ambient zones, this function should take a KRAABB and cull out any far away zones - return m_reverbZoneNodes; -} - -std::set &KRScene::getLocators() -{ - return m_locatorNodes; -} - -std::set &KRScene::getLights() -{ - return m_lights; -} - -void KRScene::render(KRCamera *pCamera, unordered_map &visibleBounds, const KRViewport &viewport, KRNode::RenderPass renderPass, bool new_frame) { - if(new_frame) { - // Expire cached occlusion test results. - // Cached "failed" results are expired on the next frame (marked with .second of -1) - // Cached "success" results are expired after KRENGINE_OCCLUSION_TEST_EXPIRY frames (marked with .second of the last frame - std::set expired_visible_bounds; - for(unordered_map::iterator visible_bounds_itr = visibleBounds.begin(); visible_bounds_itr != visibleBounds.end(); visible_bounds_itr++) { - if((*visible_bounds_itr).second == -1 || (*visible_bounds_itr).second + KRENGINE_OCCLUSION_TEST_EXPIRY < getContext().getCurrentFrame()) { - expired_visible_bounds.insert((*visible_bounds_itr).first); - } - } - for(std::set::iterator expired_visible_bounds_itr = expired_visible_bounds.begin(); expired_visible_bounds_itr != expired_visible_bounds.end(); expired_visible_bounds_itr++) { - visibleBounds.erase(*expired_visible_bounds_itr); - } - } - - if(getFirstLight() == NULL) { - addDefaultLights(); - } - - std::vector point_lights; - std::vectordirectional_lights; - std::vectorspot_lights; - - std::set outerNodes = std::set(m_nodeTree.getOuterSceneNodes()); // HACK - Copying the std::set as it is potentially modified as KRNode's update their bounds during the iteration. This is very expensive and will be eliminated in the future. - - // Get lights from outer nodes (directional lights, which have no bounds) - for(std::set::iterator itr=outerNodes.begin(); itr != outerNodes.end(); itr++) { - KRNode *node = (*itr); - KRPointLight *point_light = dynamic_cast(node); - if(point_light) { - point_lights.push_back(point_light); - } - KRDirectionalLight *directional_light = dynamic_cast(node); - if(directional_light) { - directional_lights.push_back(directional_light); - } - KRSpotLight *spot_light = dynamic_cast(node); - if(spot_light) { - spot_lights.push_back(spot_light); - } - } - - // Render outer nodes - for(std::set::iterator itr=outerNodes.begin(); itr != outerNodes.end(); itr++) { - KRNode *node = (*itr); - node->render(pCamera, point_lights, directional_lights, spot_lights, viewport, renderPass); - } - - std::vector remainingOctrees; - std::vector remainingOctreesTestResults; - std::vector remainingOctreesTestResultsOnly; - if(m_nodeTree.getRootNode() != NULL) { - remainingOctrees.push_back(m_nodeTree.getRootNode()); - } - - std::vector newRemainingOctrees; - std::vector newRemainingOctreesTestResults; - while((!remainingOctrees.empty() || !remainingOctreesTestResults.empty())) { - newRemainingOctrees.clear(); - newRemainingOctreesTestResults.clear(); - for(std::vector::iterator octree_itr = remainingOctrees.begin(); octree_itr != remainingOctrees.end(); octree_itr++) { - render(*octree_itr, visibleBounds, pCamera, point_lights, directional_lights, spot_lights, viewport, renderPass, newRemainingOctrees, newRemainingOctreesTestResults, remainingOctreesTestResultsOnly, false, false); - } - for(std::vector::iterator octree_itr = remainingOctreesTestResults.begin(); octree_itr != remainingOctreesTestResults.end(); octree_itr++) { - render(*octree_itr, visibleBounds, pCamera, point_lights, directional_lights, spot_lights, viewport, renderPass, newRemainingOctrees, newRemainingOctreesTestResults, remainingOctreesTestResultsOnly, true, false); - } - remainingOctrees = newRemainingOctrees; - remainingOctreesTestResults = newRemainingOctreesTestResults; - } - - newRemainingOctrees.clear(); - newRemainingOctreesTestResults.clear(); - for(std::vector::iterator octree_itr = remainingOctreesTestResultsOnly.begin(); octree_itr != remainingOctreesTestResultsOnly.end(); octree_itr++) { - render(*octree_itr, visibleBounds, pCamera, point_lights, directional_lights, spot_lights, viewport, renderPass, newRemainingOctrees, newRemainingOctreesTestResults, remainingOctreesTestResultsOnly, true, true); - } -} - -void KRScene::render(KROctreeNode *pOctreeNode, unordered_map &visibleBounds, KRCamera *pCamera, std::vector &point_lights, std::vector &directional_lights, std::vector&spot_lights, const KRViewport &viewport, KRNode::RenderPass renderPass, std::vector &remainingOctrees, std::vector &remainingOctreesTestResults, std::vector &remainingOctreesTestResultsOnly, bool bOcclusionResultsPass, bool bOcclusionTestResultsOnly) -{ - if(pOctreeNode) { - - AABB octreeBounds = pOctreeNode->getBounds(); - - if(bOcclusionResultsPass) { - // ----====---- Occlusion results pass ----====---- - if(pOctreeNode->m_occlusionTested) { - GLuint params = 0; - GLDEBUG(glGetQueryObjectuivEXT(pOctreeNode->m_occlusionQuery, GL_QUERY_RESULT_EXT, ¶ms)); - - if(params) { - // Record the frame number that the test has passed on - visibleBounds[octreeBounds] = getContext().getCurrentFrame(); - - if(!bOcclusionTestResultsOnly) { - // Schedule a pass to perform the rendering - remainingOctrees.push_back(pOctreeNode); - } - } else { - // Record -1 to indicate that the visibility test had failed - visibleBounds[octreeBounds] = -1; - } - - GLDEBUG(glDeleteQueriesEXT(1, &pOctreeNode->m_occlusionQuery)); - pOctreeNode->m_occlusionTested = false; - pOctreeNode->m_occlusionQuery = 0; - } - } else { - bool in_viewport = false; - if(renderPass == KRNode::RENDER_PASS_PRESTREAM) { - // When pre-streaming, objects are streamed in behind and in-front of the camera - AABB viewportExtents = AABB(viewport.getCameraPosition() - Vector3(pCamera->settings.getPerspectiveFarZ()), viewport.getCameraPosition() + Vector3(pCamera->settings.getPerspectiveFarZ())); - in_viewport = octreeBounds.intersects(viewportExtents); - } else { - in_viewport = viewport.visible(pOctreeNode->getBounds()); - } - if(in_viewport) { - - // ----====---- Rendering and occlusion test pass ----====---- - bool bVisible = false; - bool bNeedOcclusionTest = true; - - if(!pCamera->settings.getEnableRealtimeOcclusion()) { - bVisible = true; - bNeedOcclusionTest = false; - } - - if(!bVisible) { - // Assume bounding boxes are visible without occlusion test queries if the camera is inside the box. - // The near clipping plane of the camera is taken into consideration by expanding the match area - AABB cameraExtents = AABB(viewport.getCameraPosition() - Vector3(pCamera->settings.getPerspectiveNearZ()), viewport.getCameraPosition() + Vector3(pCamera->settings.getPerspectiveNearZ())); - bVisible = octreeBounds.intersects(cameraExtents); - if(bVisible) { - // Record the frame number in which the camera was within the bounds - visibleBounds[octreeBounds] = getContext().getCurrentFrame(); - bNeedOcclusionTest = false; - } - } - - - if(!bVisible) { - // Check if a previous occlusion query has returned true, taking advantage of temporal consistency of visible elements from frame to frame - // If the previous frame rendered this octree, then attempt to render it in this frame without performing a pre-occlusion test - unordered_map::iterator match_itr = visibleBounds.find(octreeBounds); - if(match_itr != visibleBounds.end()) { - if((*match_itr).second == -1) { - // We have already tested these bounds with a negative result - bNeedOcclusionTest = false; - } else { - bVisible = true; - - // We set bNeedOcclusionTest to false only when the previous occlusion test is old and we need to perform an occlusion test to record if this octree node was visible for the next frame - bNeedOcclusionTest = false; - } - } - - } - - if(!bVisible && bNeedOcclusionTest) { - // Optimization: If this is an empty octree node with only a single child node, then immediately try to render the child node without an occlusion test for this higher level, as it would be more expensive than the occlusion test for the child - if(pOctreeNode->getSceneNodes().empty()) { - int child_count = 0; - for(int i=0; i<8; i++) { - if(pOctreeNode->getChildren()[i] != NULL) child_count++; - } - if(child_count == 1) { - bVisible = true; - bNeedOcclusionTest = false; - } - } - } - - if(bNeedOcclusionTest) { - pOctreeNode->beginOcclusionQuery(); - - - - Matrix4 matModel = Matrix4(); - matModel.scale(octreeBounds.size() * 0.5f); - matModel.translate(octreeBounds.center()); - Matrix4 mvpmatrix = matModel * viewport.getViewProjectionMatrix(); - - - getContext().getMeshManager()->bindVBO(&getContext().getMeshManager()->KRENGINE_VBO_DATA_3D_CUBE_VERTICES, 1.0f); - - // Enable additive blending - if(renderPass != KRNode::RENDER_PASS_FORWARD_TRANSPARENT && renderPass != KRNode::RENDER_PASS_ADDITIVE_PARTICLES && renderPass != KRNode::RENDER_PASS_VOLUMETRIC_EFFECTS_ADDITIVE) { - GLDEBUG(glEnable(GL_BLEND)); - } - GLDEBUG(glBlendFunc(GL_ONE, GL_ONE)); - - - if(renderPass == KRNode::RENDER_PASS_FORWARD_OPAQUE || - renderPass == KRNode::RENDER_PASS_DEFERRED_GBUFFER || - renderPass == KRNode::RENDER_PASS_DEFERRED_OPAQUE || - renderPass == KRNode::RENDER_PASS_SHADOWMAP) { - - // Disable z-buffer write - GLDEBUG(glDepthMask(GL_FALSE)); - } - - if(getContext().getShaderManager()->selectShader("occlusion_test", *pCamera, point_lights, directional_lights, spot_lights, 0, viewport, matModel, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, KRNode::RENDER_PASS_FORWARD_TRANSPARENT, Vector3::Zero(), 0.0f, Vector4::Zero())) { - GLDEBUG(glDrawArrays(GL_TRIANGLE_STRIP, 0, 14)); - m_pContext->getMeshManager()->log_draw_call(renderPass, "octree", "occlusion_test", 14); - } - - if(renderPass == KRNode::RENDER_PASS_FORWARD_OPAQUE || - renderPass == KRNode::RENDER_PASS_DEFERRED_GBUFFER || - renderPass == KRNode::RENDER_PASS_DEFERRED_OPAQUE || - renderPass == KRNode::RENDER_PASS_SHADOWMAP) { - - // Re-enable z-buffer write - GLDEBUG(glDepthMask(GL_TRUE)); - } - - pOctreeNode->endOcclusionQuery(); - - if(renderPass != KRNode::RENDER_PASS_FORWARD_TRANSPARENT && renderPass != KRNode::RENDER_PASS_ADDITIVE_PARTICLES && renderPass != KRNode::RENDER_PASS_VOLUMETRIC_EFFECTS_ADDITIVE) { - GLDEBUG(glDisable(GL_BLEND)); - } else if(renderPass == KRNode::RENDER_PASS_FORWARD_TRANSPARENT) { - GLDEBUG(glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA)); - } else { - GLDEBUG(glBlendFunc(GL_ONE, GL_ONE)); // RENDER_PASS_FORWARD_TRANSPARENT and RENDER_PASS_VOLUMETRIC_EFFECTS_ADDITIVE - } - - - if(bVisible) { - // Schedule a pass to get the result of the occlusion test only for future frames and passes, without rendering the model or recurring further - remainingOctreesTestResultsOnly.push_back(pOctreeNode); - } else { - // Schedule a pass to get the result of the occlusion test and continue recursion and rendering if test is true - remainingOctreesTestResults.push_back(pOctreeNode); - } - } - - if(bVisible) { - - // Add lights that influence this octree level and its children to the stack - int directional_light_count = 0; - int spot_light_count = 0; - int point_light_count = 0; - for(std::set::iterator itr=pOctreeNode->getSceneNodes().begin(); itr != pOctreeNode->getSceneNodes().end(); itr++) { - KRNode *node = (*itr); - KRDirectionalLight *directional_light = dynamic_cast(node); - if(directional_light) { - directional_lights.push_back(directional_light); - directional_light_count++; - } - KRSpotLight *spot_light = dynamic_cast(node); - if(spot_light) { - spot_lights.push_back(spot_light); - spot_light_count++; - } - KRPointLight *point_light = dynamic_cast(node); - if(point_light) { - point_lights.push_back(point_light); - point_light_count++; - } - } - - // Render objects that are at this octree level - for(std::set::iterator itr=pOctreeNode->getSceneNodes().begin(); itr != pOctreeNode->getSceneNodes().end(); itr++) { - //assert(pOctreeNode->getBounds().contains((*itr)->getBounds())); // Sanity check - (*itr)->render(pCamera, point_lights, directional_lights, spot_lights, viewport, renderPass); - } - - // Render child octrees - const int *childOctreeOrder = renderPass == KRNode::RENDER_PASS_FORWARD_TRANSPARENT || renderPass == KRNode::RENDER_PASS_ADDITIVE_PARTICLES || renderPass == KRNode::RENDER_PASS_VOLUMETRIC_EFFECTS_ADDITIVE ? viewport.getBackToFrontOrder() : viewport.getFrontToBackOrder(); - - for(int i=0; i<8; i++) { - render(pOctreeNode->getChildren()[childOctreeOrder[i]], visibleBounds, pCamera, point_lights, directional_lights, spot_lights, viewport, renderPass, remainingOctrees, remainingOctreesTestResults, remainingOctreesTestResultsOnly, false, false); - } - - // Remove lights added at this octree level from the stack - while(directional_light_count--) { - directional_lights.pop_back(); - } - while(spot_light_count--) { - spot_lights.pop_back(); - } - while(point_light_count--) { - point_lights.pop_back(); - } - } - } - - } - } -// fprintf(stderr, "Octree culled: (%f, %f, %f) - (%f, %f, %f)\n", pOctreeNode->getBounds().min.x, pOctreeNode->getBounds().min.y, pOctreeNode->getBounds().min.z, pOctreeNode->getBounds().max.x, pOctreeNode->getBounds().max.y, pOctreeNode->getBounds().max.z); -} - -std::string KRScene::getExtension() { - return "krscene"; -} - -KRNode *KRScene::getRootNode() { - return m_pRootNode; -} - -bool KRScene::save(KRDataBlock &data) { - tinyxml2::XMLDocument doc; - tinyxml2::XMLElement *scene_node = doc.NewElement( "scene" ); - doc.InsertEndChild(scene_node); - m_pRootNode->saveXML(scene_node); - - tinyxml2::XMLPrinter p; - doc.Print(&p); - data.append((void *)p.CStr(), strlen(p.CStr())+1); - - return true; -} - -KRScene *KRScene::Load(KRContext &context, const std::string &name, KRDataBlock *data) -{ - std::string xml_string = data->getString(); - delete data; - tinyxml2::XMLDocument doc; - doc.Parse(xml_string.c_str()); - KRScene *new_scene = new KRScene(context, name); - - tinyxml2::XMLElement *scene_element = doc.RootElement(); - - KRNode *n = KRNode::LoadXML(*new_scene, scene_element->FirstChildElement()); - if(n) { - new_scene->getRootNode()->addChild(n); - } - - - return new_scene; -} - - - -KRLight *KRScene::getFirstLight() -{ - if(m_pFirstLight == NULL) { - m_pFirstLight = find(); - } - return m_pFirstLight; -} - -void KRScene::notify_sceneGraphCreate(KRNode *pNode) -{ -// m_nodeTree.add(pNode); -// if(pNode->hasPhysics()) { -// m_physicsNodes.insert(pNode); -// } - m_newNodes.insert(pNode); -} - -void KRScene::notify_sceneGraphModify(KRNode *pNode) -{ - // m_nodeTree.update(pNode); - m_modifiedNodes.insert(pNode); -} - -void KRScene::notify_sceneGraphDelete(KRNode *pNode) -{ - m_nodeTree.remove(pNode); - m_physicsNodes.erase(pNode); - KRAmbientZone *AmbientZoneNode = dynamic_cast(pNode); - if(AmbientZoneNode) { - m_ambientZoneNodes.erase(AmbientZoneNode); - } - KRReverbZone *ReverbZoneNode = dynamic_cast(pNode); - if(ReverbZoneNode) { - m_reverbZoneNodes.erase(ReverbZoneNode); - } - KRLocator *locator = dynamic_cast(pNode); - if(locator) { - m_locatorNodes.erase(locator); - } - KRLight *light = dynamic_cast(pNode); - if(light) { - m_lights.erase(light); - } - m_modifiedNodes.erase(pNode); - if(!m_newNodes.erase(pNode)) { - m_nodeTree.remove(pNode); - } -} - -void KRScene::updateOctree(const KRViewport &viewport) -{ - m_pRootNode->setLODVisibility(KRNode::LOD_VISIBILITY_VISIBLE); - m_pRootNode->updateLODVisibility(viewport); - - std::set newNodes = std::move(m_newNodes); - std::set modifiedNodes = std::move(m_modifiedNodes); - m_newNodes.clear(); - m_modifiedNodes.clear(); - - for(std::set::iterator itr=newNodes.begin(); itr != newNodes.end(); itr++) { - KRNode *node = *itr; - m_nodeTree.add(node); - if(node->hasPhysics()) { - m_physicsNodes.insert(node); - } - KRAmbientZone *ambientZoneNode = dynamic_cast(node); - if(ambientZoneNode) { - m_ambientZoneNodes.insert(ambientZoneNode); - } - KRReverbZone *reverbZoneNode = dynamic_cast(node); - if(reverbZoneNode) { - m_reverbZoneNodes.insert(reverbZoneNode); - } - KRLocator *locatorNode = dynamic_cast(node); - if(locatorNode) { - m_locatorNodes.insert(locatorNode); - } - KRLight *light = dynamic_cast(node); - if(light) { - m_lights.insert(light); - } - } - for(std::set::iterator itr=modifiedNodes.begin(); itr != modifiedNodes.end(); itr++) { - KRNode *node = *itr; - if(node->getLODVisibility() >= KRNode::LOD_VISIBILITY_PRESTREAM) { - m_nodeTree.update(node); - } - if(node->hasPhysics()) { - m_physicsNodes.insert(node); - } else if(!node->hasPhysics()) { - m_physicsNodes.erase(node); - } - } -} - -void KRScene::buildOctreeForTheFirstTime() -{ - std::set newNodes = std::move(m_newNodes); - m_newNodes.clear(); - for(std::set::iterator itr=newNodes.begin(); itr != newNodes.end(); itr++) { - KRNode *node = *itr; - m_nodeTree.add(node); - if(node->hasPhysics()) { - m_physicsNodes.insert(node); - } - KRAmbientZone *ambientZoneNode = dynamic_cast(node); - if(ambientZoneNode) { - m_ambientZoneNodes.insert(ambientZoneNode); - } - KRReverbZone *reverbZoneNode = dynamic_cast(node); - if(reverbZoneNode) { - m_reverbZoneNodes.insert(reverbZoneNode); - } - KRLocator *locatorNode = dynamic_cast(node); - if(locatorNode) { - m_locatorNodes.insert(locatorNode); - } - KRLight *light = dynamic_cast(node); - if(light) { - m_lights.insert(light); - } - } -} - -void KRScene::physicsUpdate(float deltaTime) -{ - for(std::set::iterator itr=m_physicsNodes.begin(); itr != m_physicsNodes.end(); itr++) { - (*itr)->physicsUpdate(deltaTime); - } -} - -void KRScene::addDefaultLights() -{ - KRDirectionalLight *light1 = new KRDirectionalLight(*this, "default_light1"); - - light1->setLocalRotation((Quaternion(Vector3(0.0, M_PI * 0.10, 0.0)) * Quaternion(Vector3(0.0, 0.0, -M_PI * 0.15))).eulerXYZ()); - m_pRootNode->addChild(light1); -} - -AABB KRScene::getRootOctreeBounds() -{ - if(m_nodeTree.getRootNode()) { - return m_nodeTree.getRootNode()->getBounds(); - } else { - return AABB(-Vector3::One(), Vector3::One()); - } -} - - -bool KRScene::lineCast(const Vector3 &v0, const Vector3 &v1, HitInfo &hitinfo, unsigned int layer_mask) -{ - return m_nodeTree.lineCast(v0, v1, hitinfo, layer_mask); -} - -bool KRScene::rayCast(const Vector3 &v0, const Vector3 &dir, HitInfo &hitinfo, unsigned int layer_mask) -{ - return m_nodeTree.rayCast(v0, dir, hitinfo, layer_mask); -} - -bool KRScene::sphereCast(const Vector3 &v0, const Vector3 &v1, float radius, HitInfo &hitinfo, unsigned int layer_mask) -{ - return m_nodeTree.sphereCast(v0, v1, radius, hitinfo, layer_mask); -} - - -kraken_stream_level KRScene::getStreamLevel() -{ - kraken_stream_level stream_level = kraken_stream_level::STREAM_LEVEL_IN_HQ; - - if(m_pRootNode) { - KRViewport viewport; // This isn't used when prime is false - stream_level = KRMIN(stream_level, m_pRootNode->getStreamLevel(viewport)); - } - - return stream_level; -} +// +// KRScene.cpp +// KREngine +// +// Copyright 2012 Kearwood Gilbert. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other materials +// provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// The views and conclusions contained in the software and documentation are those of the +// authors and should not be interpreted as representing official policies, either expressed +// or implied, of Kearwood Gilbert. +// + +#include "KREngine-common.h" + +#include "KRLight.h" + +#include "KRScene.h" +#include "KRNode.h" +#include "KRStockGeometry.h" +#include "KRDirectionalLight.h" +#include "KRSpotLight.h" +#include "KRPointLight.h" +#include "KRAudioManager.h" + +const long KRENGINE_OCCLUSION_TEST_EXPIRY = 10; + +KRScene::KRScene(KRContext &context, std::string name) : KRResource(context, name) { + m_pFirstLight = NULL; + m_pRootNode = new KRNode(*this, "scene_root"); + notify_sceneGraphCreate(m_pRootNode); +} + +KRScene::~KRScene() { + delete m_pRootNode; + m_pRootNode = NULL; +} + +void KRScene::renderFrame(GLint defaultFBO, float deltaTime, int width, int height) { + getContext().startFrame(deltaTime); + KRCamera *camera = find("default_camera"); + if(camera == NULL) { + // Add a default camera if none are present + camera = new KRCamera(*this, "default_camera"); + m_pRootNode->addChild(camera); + } + + // FINDME - This should be moved to de-couple Siren from the Rendering pipeline + getContext().getAudioManager()->setEnableAudio(camera->settings.siren_enable); + getContext().getAudioManager()->setEnableHRTF(camera->settings.siren_enable_hrtf); + getContext().getAudioManager()->setEnableReverb(camera->settings.siren_enable_reverb); + getContext().getAudioManager()->setReverbMaxLength(camera->settings.siren_reverb_max_length); + getContext().getTextureManager()->setMaxAnisotropy(camera->settings.max_anisotropy); + + camera->renderFrame(defaultFBO, width, height); + getContext().endFrame(deltaTime); + physicsUpdate(deltaTime); +} + +std::set &KRScene::getAmbientZones() +{ + // FINDME, TODO - To support large scenes with many reverb / ambient zones, this function should take a KRAABB and cull out any far away zones + return m_ambientZoneNodes; +} + +std::set &KRScene::getReverbZones() +{ + // FINDME, TODO - To support large scenes with many reverb / ambient zones, this function should take a KRAABB and cull out any far away zones + return m_reverbZoneNodes; +} + +std::set &KRScene::getLocators() +{ + return m_locatorNodes; +} + +std::set &KRScene::getLights() +{ + return m_lights; +} + +void KRScene::render(KRCamera *pCamera, unordered_map &visibleBounds, const KRViewport &viewport, KRNode::RenderPass renderPass, bool new_frame) { + if(new_frame) { + // Expire cached occlusion test results. + // Cached "failed" results are expired on the next frame (marked with .second of -1) + // Cached "success" results are expired after KRENGINE_OCCLUSION_TEST_EXPIRY frames (marked with .second of the last frame + std::set expired_visible_bounds; + for(unordered_map::iterator visible_bounds_itr = visibleBounds.begin(); visible_bounds_itr != visibleBounds.end(); visible_bounds_itr++) { + if((*visible_bounds_itr).second == -1 || (*visible_bounds_itr).second + KRENGINE_OCCLUSION_TEST_EXPIRY < getContext().getCurrentFrame()) { + expired_visible_bounds.insert((*visible_bounds_itr).first); + } + } + for(std::set::iterator expired_visible_bounds_itr = expired_visible_bounds.begin(); expired_visible_bounds_itr != expired_visible_bounds.end(); expired_visible_bounds_itr++) { + visibleBounds.erase(*expired_visible_bounds_itr); + } + } + + if(getFirstLight() == NULL) { + addDefaultLights(); + } + + std::vector point_lights; + std::vectordirectional_lights; + std::vectorspot_lights; + + std::set outerNodes = std::set(m_nodeTree.getOuterSceneNodes()); // HACK - Copying the std::set as it is potentially modified as KRNode's update their bounds during the iteration. This is very expensive and will be eliminated in the future. + + // Get lights from outer nodes (directional lights, which have no bounds) + for(std::set::iterator itr=outerNodes.begin(); itr != outerNodes.end(); itr++) { + KRNode *node = (*itr); + KRPointLight *point_light = dynamic_cast(node); + if(point_light) { + point_lights.push_back(point_light); + } + KRDirectionalLight *directional_light = dynamic_cast(node); + if(directional_light) { + directional_lights.push_back(directional_light); + } + KRSpotLight *spot_light = dynamic_cast(node); + if(spot_light) { + spot_lights.push_back(spot_light); + } + } + + // Render outer nodes + for(std::set::iterator itr=outerNodes.begin(); itr != outerNodes.end(); itr++) { + KRNode *node = (*itr); + node->render(pCamera, point_lights, directional_lights, spot_lights, viewport, renderPass); + } + + std::vector remainingOctrees; + std::vector remainingOctreesTestResults; + std::vector remainingOctreesTestResultsOnly; + if(m_nodeTree.getRootNode() != NULL) { + remainingOctrees.push_back(m_nodeTree.getRootNode()); + } + + std::vector newRemainingOctrees; + std::vector newRemainingOctreesTestResults; + while((!remainingOctrees.empty() || !remainingOctreesTestResults.empty())) { + newRemainingOctrees.clear(); + newRemainingOctreesTestResults.clear(); + for(std::vector::iterator octree_itr = remainingOctrees.begin(); octree_itr != remainingOctrees.end(); octree_itr++) { + render(*octree_itr, visibleBounds, pCamera, point_lights, directional_lights, spot_lights, viewport, renderPass, newRemainingOctrees, newRemainingOctreesTestResults, remainingOctreesTestResultsOnly, false, false); + } + for(std::vector::iterator octree_itr = remainingOctreesTestResults.begin(); octree_itr != remainingOctreesTestResults.end(); octree_itr++) { + render(*octree_itr, visibleBounds, pCamera, point_lights, directional_lights, spot_lights, viewport, renderPass, newRemainingOctrees, newRemainingOctreesTestResults, remainingOctreesTestResultsOnly, true, false); + } + remainingOctrees = newRemainingOctrees; + remainingOctreesTestResults = newRemainingOctreesTestResults; + } + + newRemainingOctrees.clear(); + newRemainingOctreesTestResults.clear(); + for(std::vector::iterator octree_itr = remainingOctreesTestResultsOnly.begin(); octree_itr != remainingOctreesTestResultsOnly.end(); octree_itr++) { + render(*octree_itr, visibleBounds, pCamera, point_lights, directional_lights, spot_lights, viewport, renderPass, newRemainingOctrees, newRemainingOctreesTestResults, remainingOctreesTestResultsOnly, true, true); + } +} + +void KRScene::render(KROctreeNode *pOctreeNode, unordered_map &visibleBounds, KRCamera *pCamera, std::vector &point_lights, std::vector &directional_lights, std::vector&spot_lights, const KRViewport &viewport, KRNode::RenderPass renderPass, std::vector &remainingOctrees, std::vector &remainingOctreesTestResults, std::vector &remainingOctreesTestResultsOnly, bool bOcclusionResultsPass, bool bOcclusionTestResultsOnly) +{ + if(pOctreeNode) { + + AABB octreeBounds = pOctreeNode->getBounds(); + + if(bOcclusionResultsPass) { + // ----====---- Occlusion results pass ----====---- + if(pOctreeNode->m_occlusionTested) { + GLuint params = 0; + GLDEBUG(glGetQueryObjectuivEXT(pOctreeNode->m_occlusionQuery, GL_QUERY_RESULT_EXT, ¶ms)); + + if(params) { + // Record the frame number that the test has passed on + visibleBounds[octreeBounds] = getContext().getCurrentFrame(); + + if(!bOcclusionTestResultsOnly) { + // Schedule a pass to perform the rendering + remainingOctrees.push_back(pOctreeNode); + } + } else { + // Record -1 to indicate that the visibility test had failed + visibleBounds[octreeBounds] = -1; + } + + GLDEBUG(glDeleteQueriesEXT(1, &pOctreeNode->m_occlusionQuery)); + pOctreeNode->m_occlusionTested = false; + pOctreeNode->m_occlusionQuery = 0; + } + } else { + bool in_viewport = false; + if(renderPass == KRNode::RENDER_PASS_PRESTREAM) { + // When pre-streaming, objects are streamed in behind and in-front of the camera + AABB viewportExtents = AABB::Create(viewport.getCameraPosition() - Vector3::Create(pCamera->settings.getPerspectiveFarZ()), viewport.getCameraPosition() + Vector3::Create(pCamera->settings.getPerspectiveFarZ())); + in_viewport = octreeBounds.intersects(viewportExtents); + } else { + in_viewport = viewport.visible(pOctreeNode->getBounds()); + } + if(in_viewport) { + + // ----====---- Rendering and occlusion test pass ----====---- + bool bVisible = false; + bool bNeedOcclusionTest = true; + + if(!pCamera->settings.getEnableRealtimeOcclusion()) { + bVisible = true; + bNeedOcclusionTest = false; + } + + if(!bVisible) { + // Assume bounding boxes are visible without occlusion test queries if the camera is inside the box. + // The near clipping plane of the camera is taken into consideration by expanding the match area + AABB cameraExtents = AABB::Create(viewport.getCameraPosition() - Vector3::Create(pCamera->settings.getPerspectiveNearZ()), viewport.getCameraPosition() + Vector3::Create(pCamera->settings.getPerspectiveNearZ())); + bVisible = octreeBounds.intersects(cameraExtents); + if(bVisible) { + // Record the frame number in which the camera was within the bounds + visibleBounds[octreeBounds] = getContext().getCurrentFrame(); + bNeedOcclusionTest = false; + } + } + + + if(!bVisible) { + // Check if a previous occlusion query has returned true, taking advantage of temporal consistency of visible elements from frame to frame + // If the previous frame rendered this octree, then attempt to render it in this frame without performing a pre-occlusion test + unordered_map::iterator match_itr = visibleBounds.find(octreeBounds); + if(match_itr != visibleBounds.end()) { + if((*match_itr).second == -1) { + // We have already tested these bounds with a negative result + bNeedOcclusionTest = false; + } else { + bVisible = true; + + // We set bNeedOcclusionTest to false only when the previous occlusion test is old and we need to perform an occlusion test to record if this octree node was visible for the next frame + bNeedOcclusionTest = false; + } + } + + } + + if(!bVisible && bNeedOcclusionTest) { + // Optimization: If this is an empty octree node with only a single child node, then immediately try to render the child node without an occlusion test for this higher level, as it would be more expensive than the occlusion test for the child + if(pOctreeNode->getSceneNodes().empty()) { + int child_count = 0; + for(int i=0; i<8; i++) { + if(pOctreeNode->getChildren()[i] != NULL) child_count++; + } + if(child_count == 1) { + bVisible = true; + bNeedOcclusionTest = false; + } + } + } + + if(bNeedOcclusionTest) { + pOctreeNode->beginOcclusionQuery(); + + + + Matrix4 matModel = Matrix4(); + matModel.scale(octreeBounds.size() * 0.5f); + matModel.translate(octreeBounds.center()); + Matrix4 mvpmatrix = matModel * viewport.getViewProjectionMatrix(); + + + getContext().getMeshManager()->bindVBO(&getContext().getMeshManager()->KRENGINE_VBO_DATA_3D_CUBE_VERTICES, 1.0f); + + // Enable additive blending + if(renderPass != KRNode::RENDER_PASS_FORWARD_TRANSPARENT && renderPass != KRNode::RENDER_PASS_ADDITIVE_PARTICLES && renderPass != KRNode::RENDER_PASS_VOLUMETRIC_EFFECTS_ADDITIVE) { + GLDEBUG(glEnable(GL_BLEND)); + } + GLDEBUG(glBlendFunc(GL_ONE, GL_ONE)); + + + if(renderPass == KRNode::RENDER_PASS_FORWARD_OPAQUE || + renderPass == KRNode::RENDER_PASS_DEFERRED_GBUFFER || + renderPass == KRNode::RENDER_PASS_DEFERRED_OPAQUE || + renderPass == KRNode::RENDER_PASS_SHADOWMAP) { + + // Disable z-buffer write + GLDEBUG(glDepthMask(GL_FALSE)); + } + + if(getContext().getShaderManager()->selectShader("occlusion_test", *pCamera, point_lights, directional_lights, spot_lights, 0, viewport, matModel, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, KRNode::RENDER_PASS_FORWARD_TRANSPARENT, Vector3::Zero(), 0.0f, Vector4::Zero())) { + GLDEBUG(glDrawArrays(GL_TRIANGLE_STRIP, 0, 14)); + m_pContext->getMeshManager()->log_draw_call(renderPass, "octree", "occlusion_test", 14); + } + + if(renderPass == KRNode::RENDER_PASS_FORWARD_OPAQUE || + renderPass == KRNode::RENDER_PASS_DEFERRED_GBUFFER || + renderPass == KRNode::RENDER_PASS_DEFERRED_OPAQUE || + renderPass == KRNode::RENDER_PASS_SHADOWMAP) { + + // Re-enable z-buffer write + GLDEBUG(glDepthMask(GL_TRUE)); + } + + pOctreeNode->endOcclusionQuery(); + + if(renderPass != KRNode::RENDER_PASS_FORWARD_TRANSPARENT && renderPass != KRNode::RENDER_PASS_ADDITIVE_PARTICLES && renderPass != KRNode::RENDER_PASS_VOLUMETRIC_EFFECTS_ADDITIVE) { + GLDEBUG(glDisable(GL_BLEND)); + } else if(renderPass == KRNode::RENDER_PASS_FORWARD_TRANSPARENT) { + GLDEBUG(glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA)); + } else { + GLDEBUG(glBlendFunc(GL_ONE, GL_ONE)); // RENDER_PASS_FORWARD_TRANSPARENT and RENDER_PASS_VOLUMETRIC_EFFECTS_ADDITIVE + } + + + if(bVisible) { + // Schedule a pass to get the result of the occlusion test only for future frames and passes, without rendering the model or recurring further + remainingOctreesTestResultsOnly.push_back(pOctreeNode); + } else { + // Schedule a pass to get the result of the occlusion test and continue recursion and rendering if test is true + remainingOctreesTestResults.push_back(pOctreeNode); + } + } + + if(bVisible) { + + // Add lights that influence this octree level and its children to the stack + int directional_light_count = 0; + int spot_light_count = 0; + int point_light_count = 0; + for(std::set::iterator itr=pOctreeNode->getSceneNodes().begin(); itr != pOctreeNode->getSceneNodes().end(); itr++) { + KRNode *node = (*itr); + KRDirectionalLight *directional_light = dynamic_cast(node); + if(directional_light) { + directional_lights.push_back(directional_light); + directional_light_count++; + } + KRSpotLight *spot_light = dynamic_cast(node); + if(spot_light) { + spot_lights.push_back(spot_light); + spot_light_count++; + } + KRPointLight *point_light = dynamic_cast(node); + if(point_light) { + point_lights.push_back(point_light); + point_light_count++; + } + } + + // Render objects that are at this octree level + for(std::set::iterator itr=pOctreeNode->getSceneNodes().begin(); itr != pOctreeNode->getSceneNodes().end(); itr++) { + //assert(pOctreeNode->getBounds().contains((*itr)->getBounds())); // Sanity check + (*itr)->render(pCamera, point_lights, directional_lights, spot_lights, viewport, renderPass); + } + + // Render child octrees + const int *childOctreeOrder = renderPass == KRNode::RENDER_PASS_FORWARD_TRANSPARENT || renderPass == KRNode::RENDER_PASS_ADDITIVE_PARTICLES || renderPass == KRNode::RENDER_PASS_VOLUMETRIC_EFFECTS_ADDITIVE ? viewport.getBackToFrontOrder() : viewport.getFrontToBackOrder(); + + for(int i=0; i<8; i++) { + render(pOctreeNode->getChildren()[childOctreeOrder[i]], visibleBounds, pCamera, point_lights, directional_lights, spot_lights, viewport, renderPass, remainingOctrees, remainingOctreesTestResults, remainingOctreesTestResultsOnly, false, false); + } + + // Remove lights added at this octree level from the stack + while(directional_light_count--) { + directional_lights.pop_back(); + } + while(spot_light_count--) { + spot_lights.pop_back(); + } + while(point_light_count--) { + point_lights.pop_back(); + } + } + } + + } + } +// fprintf(stderr, "Octree culled: (%f, %f, %f) - (%f, %f, %f)\n", pOctreeNode->getBounds().min.x, pOctreeNode->getBounds().min.y, pOctreeNode->getBounds().min.z, pOctreeNode->getBounds().max.x, pOctreeNode->getBounds().max.y, pOctreeNode->getBounds().max.z); +} + +std::string KRScene::getExtension() { + return "krscene"; +} + +KRNode *KRScene::getRootNode() { + return m_pRootNode; +} + +bool KRScene::save(KRDataBlock &data) { + tinyxml2::XMLDocument doc; + tinyxml2::XMLElement *scene_node = doc.NewElement( "scene" ); + doc.InsertEndChild(scene_node); + m_pRootNode->saveXML(scene_node); + + tinyxml2::XMLPrinter p; + doc.Print(&p); + data.append((void *)p.CStr(), strlen(p.CStr())+1); + + return true; +} + +KRScene *KRScene::Load(KRContext &context, const std::string &name, KRDataBlock *data) +{ + std::string xml_string = data->getString(); + delete data; + tinyxml2::XMLDocument doc; + doc.Parse(xml_string.c_str()); + KRScene *new_scene = new KRScene(context, name); + + tinyxml2::XMLElement *scene_element = doc.RootElement(); + + KRNode *n = KRNode::LoadXML(*new_scene, scene_element->FirstChildElement()); + if(n) { + new_scene->getRootNode()->addChild(n); + } + + + return new_scene; +} + + + +KRLight *KRScene::getFirstLight() +{ + if(m_pFirstLight == NULL) { + m_pFirstLight = find(); + } + return m_pFirstLight; +} + +void KRScene::notify_sceneGraphCreate(KRNode *pNode) +{ +// m_nodeTree.add(pNode); +// if(pNode->hasPhysics()) { +// m_physicsNodes.insert(pNode); +// } + m_newNodes.insert(pNode); +} + +void KRScene::notify_sceneGraphModify(KRNode *pNode) +{ + // m_nodeTree.update(pNode); + m_modifiedNodes.insert(pNode); +} + +void KRScene::notify_sceneGraphDelete(KRNode *pNode) +{ + m_nodeTree.remove(pNode); + m_physicsNodes.erase(pNode); + KRAmbientZone *AmbientZoneNode = dynamic_cast(pNode); + if(AmbientZoneNode) { + m_ambientZoneNodes.erase(AmbientZoneNode); + } + KRReverbZone *ReverbZoneNode = dynamic_cast(pNode); + if(ReverbZoneNode) { + m_reverbZoneNodes.erase(ReverbZoneNode); + } + KRLocator *locator = dynamic_cast(pNode); + if(locator) { + m_locatorNodes.erase(locator); + } + KRLight *light = dynamic_cast(pNode); + if(light) { + m_lights.erase(light); + } + m_modifiedNodes.erase(pNode); + if(!m_newNodes.erase(pNode)) { + m_nodeTree.remove(pNode); + } +} + +void KRScene::updateOctree(const KRViewport &viewport) +{ + m_pRootNode->setLODVisibility(KRNode::LOD_VISIBILITY_VISIBLE); + m_pRootNode->updateLODVisibility(viewport); + + std::set newNodes = std::move(m_newNodes); + std::set modifiedNodes = std::move(m_modifiedNodes); + m_newNodes.clear(); + m_modifiedNodes.clear(); + + for(std::set::iterator itr=newNodes.begin(); itr != newNodes.end(); itr++) { + KRNode *node = *itr; + m_nodeTree.add(node); + if(node->hasPhysics()) { + m_physicsNodes.insert(node); + } + KRAmbientZone *ambientZoneNode = dynamic_cast(node); + if(ambientZoneNode) { + m_ambientZoneNodes.insert(ambientZoneNode); + } + KRReverbZone *reverbZoneNode = dynamic_cast(node); + if(reverbZoneNode) { + m_reverbZoneNodes.insert(reverbZoneNode); + } + KRLocator *locatorNode = dynamic_cast(node); + if(locatorNode) { + m_locatorNodes.insert(locatorNode); + } + KRLight *light = dynamic_cast(node); + if(light) { + m_lights.insert(light); + } + } + for(std::set::iterator itr=modifiedNodes.begin(); itr != modifiedNodes.end(); itr++) { + KRNode *node = *itr; + if(node->getLODVisibility() >= KRNode::LOD_VISIBILITY_PRESTREAM) { + m_nodeTree.update(node); + } + if(node->hasPhysics()) { + m_physicsNodes.insert(node); + } else if(!node->hasPhysics()) { + m_physicsNodes.erase(node); + } + } +} + +void KRScene::buildOctreeForTheFirstTime() +{ + std::set newNodes = std::move(m_newNodes); + m_newNodes.clear(); + for(std::set::iterator itr=newNodes.begin(); itr != newNodes.end(); itr++) { + KRNode *node = *itr; + m_nodeTree.add(node); + if(node->hasPhysics()) { + m_physicsNodes.insert(node); + } + KRAmbientZone *ambientZoneNode = dynamic_cast(node); + if(ambientZoneNode) { + m_ambientZoneNodes.insert(ambientZoneNode); + } + KRReverbZone *reverbZoneNode = dynamic_cast(node); + if(reverbZoneNode) { + m_reverbZoneNodes.insert(reverbZoneNode); + } + KRLocator *locatorNode = dynamic_cast(node); + if(locatorNode) { + m_locatorNodes.insert(locatorNode); + } + KRLight *light = dynamic_cast(node); + if(light) { + m_lights.insert(light); + } + } +} + +void KRScene::physicsUpdate(float deltaTime) +{ + for(std::set::iterator itr=m_physicsNodes.begin(); itr != m_physicsNodes.end(); itr++) { + (*itr)->physicsUpdate(deltaTime); + } +} + +void KRScene::addDefaultLights() +{ + KRDirectionalLight *light1 = new KRDirectionalLight(*this, "default_light1"); + + light1->setLocalRotation((Quaternion::Create(Vector3::Create(0.0f, M_PI * 0.10f, 0.0f)) * Quaternion::Create(Vector3::Create(0.0f, 0.0f, -M_PI * 0.15f))).eulerXYZ()); + m_pRootNode->addChild(light1); +} + +AABB KRScene::getRootOctreeBounds() +{ + if(m_nodeTree.getRootNode()) { + return m_nodeTree.getRootNode()->getBounds(); + } else { + return AABB::Create(-Vector3::One(), Vector3::One()); + } +} + + +bool KRScene::lineCast(const Vector3 &v0, const Vector3 &v1, HitInfo &hitinfo, unsigned int layer_mask) +{ + return m_nodeTree.lineCast(v0, v1, hitinfo, layer_mask); +} + +bool KRScene::rayCast(const Vector3 &v0, const Vector3 &dir, HitInfo &hitinfo, unsigned int layer_mask) +{ + return m_nodeTree.rayCast(v0, dir, hitinfo, layer_mask); +} + +bool KRScene::sphereCast(const Vector3 &v0, const Vector3 &v1, float radius, HitInfo &hitinfo, unsigned int layer_mask) +{ + return m_nodeTree.sphereCast(v0, v1, radius, hitinfo, layer_mask); +} + + +kraken_stream_level KRScene::getStreamLevel() +{ + kraken_stream_level stream_level = kraken_stream_level::STREAM_LEVEL_IN_HQ; + + if(m_pRootNode) { + KRViewport viewport; // This isn't used when prime is false + stream_level = KRMIN(stream_level, m_pRootNode->getStreamLevel(viewport)); + } + + return stream_level; +} diff --git a/kraken/KRShader.cpp b/kraken/KRShader.cpp index 22f046a..0692ee5 100755 --- a/kraken/KRShader.cpp +++ b/kraken/KRShader.cpp @@ -1,596 +1,596 @@ -// -// KRShader.cpp -// KREngine -// -// Copyright 2012 Kearwood Gilbert. All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, are -// permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of -// conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list -// of conditions and the following disclaimer in the documentation and/or other materials -// provided with the distribution. -// -// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED -// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR -// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// The views and conclusions contained in the software and documentation are those of the -// authors and should not be interpreted as representing official policies, either expressed -// or implied, of Kearwood Gilbert. -// - -#include "KRShader.h" -#include "assert.h" -#include "KRLight.h" -#include "KRDirectionalLight.h" -#include "KRSpotLight.h" -#include "KRPointLight.h" - - -const char *KRShader::KRENGINE_UNIFORM_NAMES[] = { - "material_ambient", // KRENGINE_UNIFORM_MATERIAL_AMBIENT - "material_diffuse", // KRENGINE_UNIFORM_MATERIAL_DIFFUSE - "material_specular", // KRENGINE_UNIFORM_MATERIAL_SPECULAR - "material_reflection", // KRENGINE_UNIFORM_MATERIAL_REFLECTION - "material_alpha", // KRENGINE_UNIFORM_MATERIAL_ALPHA - "material_shininess", // KRENGINE_UNIFORM_MATERIAL_SHININESS - "light_position", // KRENGINE_UNIFORM_LIGHT_POSITION - "light_direction_model_space", // KRENGINE_UNIFORM_LIGHT_DIRECTION_MODEL_SPACE - "light_direction_view_space", // KRENGINE_UNIFORM_LIGHT_DIRECTION_VIEW_SPACE - "light_color", // KRENGINE_UNIFORM_LIGHT_COLOR - "light_decay_start", // KRENGINE_UNIFORM_LIGHT_DECAY_START - "light_cutoff", // KRENGINE_UNIFORM_LIGHT_CUTOFF - "light_intensity", // KRENGINE_UNIFORM_LIGHT_INTENSITY - "flare_size", // KRENGINE_UNIFORM_FLARE_SIZE - "view_space_model_origin", // KRENGINE_UNIFORM_VIEW_SPACE_MODEL_ORIGIN - "mvp_matrix", // KRENGINE_UNIFORM_MVP - "inv_projection_matrix", // KRENGINE_UNIFORM_INVP - "inv_mvp_matrix", // KRENGINE_UNIFORM_INVMVP - "inv_mvp_matrix_no_translate", // KRENGINE_UNIFORM_INVMVP_NO_TRANSLATE - "model_view_inverse_transpose_matrix", // KRENGINE_UNIFORM_MODEL_VIEW_INVERSE_TRANSPOSE - "model_inverse_transpose_matrix", // KRENGINE_UNIFORM_MODEL_INVERSE_TRANSPOSE - "model_view_matrix", // KRENGINE_UNIFORM_MODEL_VIEW - "model_matrix", // KRENGINE_UNIFORM_MODEL_MATRIX - "projection_matrix", // KRENGINE_UNIFORM_PROJECTION_MATRIX - "camera_position_model_space", // KRENGINE_UNIFORM_CAMERAPOS_MODEL_SPACE - "viewport", // KRENGINE_UNIFORM_VIEWPORT - "viewport_downsample", // KRENGINE_UNIFORM_VIEWPORT_DOWNSAMPLE - "diffuseTexture", // KRENGINE_UNIFORM_DIFFUSETEXTURE - "specularTexture", // KRENGINE_UNIFORM_SPECULARTEXTURE - "reflectionCubeTexture", // KRENGINE_UNIFORM_REFLECTIONCUBETEXTURE - "reflectionTexture", // KRENGINE_UNIFORM_REFLECTIONTEXTURE - "normalTexture", // KRENGINE_UNIFORM_NORMALTEXTURE - "diffuseTexture_Scale", // KRENGINE_UNIFORM_DIFFUSETEXTURE_SCALE - "specularTexture_Scale", // KRENGINE_UNIFORM_SPECULARTEXTURE_SCALE - "reflectionTexture_Scale", // KRENGINE_UNIFORM_REFLECTIONTEXTURE_SCALE - "normalTexture_Scale", // KRENGINE_UNIFORM_NORMALTEXTURE_SCALE - "normalTexture_Scale", // KRENGINE_UNIFORM_AMBIENTTEXTURE_SCALE - "diffuseTexture_Offset", // KRENGINE_UNIFORM_DIFFUSETEXTURE_OFFSET - "specularTexture_Offset", // KRENGINE_UNIFORM_SPECULARTEXTURE_OFFSET - "reflectionTexture_Offset", // KRENGINE_UNIFORM_REFLECTIONTEXTURE_OFFSET - "normalTexture_Offset", // KRENGINE_UNIFORM_NORMALTEXTURE_OFFSET - "ambientTexture_Offset", // KRENGINE_UNIFORM_AMBIENTTEXTURE_OFFSET - "shadow_mvp1", // KRENGINE_UNIFORM_SHADOWMVP1 - "shadow_mvp2", // KRENGINE_UNIFORM_SHADOWMVP2 - "shadow_mvp3", // KRENGINE_UNIFORM_SHADOWMVP3 - "shadowTexture1", // KRENGINE_UNIFORM_SHADOWTEXTURE1 - "shadowTexture2", // KRENGINE_UNIFORM_SHADOWTEXTURE2 - "shadowTexture3", // KRENGINE_UNIFORM_SHADOWTEXTURE3 - "lightmapTexture", // KRENGINE_UNIFORM_LIGHTMAPTEXTURE - "gbuffer_frame", // KRENGINE_UNIFORM_GBUFFER_FRAME - "gbuffer_depth", // KRENGINE_UNIFORM_GBUFFER_DEPTH - "depthFrame", // KRENGINE_UNIFORM_DEPTH_FRAME - "volumetricEnvironmentFrame", // KRENGINE_UNIFORM_VOLUMETRIC_ENVIRONMENT_FRAME - "renderFrame", // KRENGINE_UNIFORM_RENDER_FRAME - "time_absolute", // KRENGINE_UNIFORM_ABSOLUTE_TIME - "fog_near", // KRENGINE_UNIFORM_FOG_NEAR - "fog_far", // KRENGINE_UNIFORM_FOG_FAR - "fog_density", // KRENGINE_UNIFORM_FOG_DENSITY - "fog_color", // KRENGINE_UNIFORM_FOG_COLOR - "fog_scale", // KRENGINE_UNIFORM_FOG_SCALE - "fog_density_premultiplied_exponential", // KRENGINE_UNIFORM_DENSITY_PREMULTIPLIED_EXPONENTIAL - "fog_density_premultiplied_squared", // KRENGINE_UNIFORM_DENSITY_PREMULTIPLIED_SQUARED - "slice_depth_scale", // KRENGINE_UNIFORM_SLICE_DEPTH_SCALE - "particle_origin", // KRENGINE_UNIFORM_PARTICLE_ORIGIN - "bone_transforms", // KRENGINE_UNIFORM_BONE_TRANSFORMS - "rim_color", // KRENGINE_UNIFORM_RIM_COLOR - "rim_power", // KRENGINE_UNIFORM_RIM_POWER - "fade_color", // KRENGINE_UNIFORM_FADE_COLOR -}; - -KRShader::KRShader(KRContext &context, char *szKey, std::string options, std::string vertShaderSource, const std::string fragShaderSource) : KRContextObject(context) -{ - strcpy(m_szKey, szKey); - m_iProgram = 0; - - - GLuint vertexShader = 0, fragShader = 0; - try { - const GLchar *vertSource[2] = {options.c_str(), vertShaderSource.c_str()}; - const GLchar *fragSource[2] = {options.c_str(), fragShaderSource.c_str()}; - - // Create shader program. - GLDEBUG(m_iProgram = glCreateProgram()); - - // Create and compile vertex shader. - GLDEBUG(vertexShader = glCreateShader(GL_VERTEX_SHADER)); - GLDEBUG(glShaderSource(vertexShader, 2, vertSource, NULL)); - GLDEBUG(glCompileShader(vertexShader)); - - // Report any compile issues to stderr - GLint logLength = 0; - GLDEBUG(glGetShaderiv(vertexShader, GL_INFO_LOG_LENGTH, &logLength)); - if (logLength > 0) { - GLchar *log = (GLchar *)malloc(logLength + 1); - assert(log != NULL); - log[0] = '\0'; // In case glGetShaderInfoLog fails - GLDEBUG(glGetShaderInfoLog(vertexShader, logLength, &logLength, log)); - log[logLength] = '\0'; - KRContext::Log(KRContext::LOG_LEVEL_ERROR, "KREngine - Failed to compile vertex shader: %s\nShader compile log:\n%s", szKey, log); - free(log); - } - - - // Create and compile vertex shader. - GLDEBUG(fragShader = glCreateShader(GL_FRAGMENT_SHADER)); - GLDEBUG(glShaderSource(fragShader, 2, fragSource, NULL)); - GLDEBUG(glCompileShader(fragShader)); - - // Report any compile issues to stderr - logLength = 0; // In case glGetShaderiv fails - GLDEBUG(glGetShaderiv(fragShader, GL_INFO_LOG_LENGTH, &logLength)); - if (logLength > 0) { - GLchar *log = (GLchar *)malloc(logLength + 1); - assert(log != NULL); - log[0] = '\0'; // In case glGetShaderInfoLog fails - GLDEBUG(glGetShaderInfoLog(fragShader, logLength, &logLength, log)); - log[logLength] = '\0'; - KRContext::Log(KRContext::LOG_LEVEL_ERROR, "KREngine - Failed to compile fragment shader: %s\nShader compile log:\n%s", szKey, log); - free(log); - } - - // Attach vertex shader to program. - GLDEBUG(glAttachShader(m_iProgram, vertexShader)); - - // Attach fragment shader to program. - GLDEBUG(glAttachShader(m_iProgram, fragShader)); - - // Bind attribute locations. - // This needs to be done prior to linking. - GLDEBUG(glBindAttribLocation(m_iProgram, KRMesh::KRENGINE_ATTRIB_VERTEX, "vertex_position")); - GLDEBUG(glBindAttribLocation(m_iProgram, KRMesh::KRENGINE_ATTRIB_NORMAL, "vertex_normal")); - GLDEBUG(glBindAttribLocation(m_iProgram, KRMesh::KRENGINE_ATTRIB_TANGENT, "vertex_tangent")); - GLDEBUG(glBindAttribLocation(m_iProgram, KRMesh::KRENGINE_ATTRIB_TEXUVA, "vertex_uv")); - GLDEBUG(glBindAttribLocation(m_iProgram, KRMesh::KRENGINE_ATTRIB_TEXUVB, "vertex_lightmap_uv")); - GLDEBUG(glBindAttribLocation(m_iProgram, KRMesh::KRENGINE_ATTRIB_BONEINDEXES, "bone_indexes")); - GLDEBUG(glBindAttribLocation(m_iProgram, KRMesh::KRENGINE_ATTRIB_BONEWEIGHTS, "bone_weights")); - - // Link program. - GLDEBUG(glLinkProgram(m_iProgram)); - - GLint link_success = GL_FALSE; - GLDEBUG(glGetProgramiv(m_iProgram, GL_LINK_STATUS, &link_success)); - - if(link_success != GL_TRUE) { - // Report any linking issues to stderr - KRContext::Log(KRContext::LOG_LEVEL_ERROR, "KREngine - Failed to link shader program: %s", szKey); - logLength = 0; // In case glGetProgramiv fails - GLDEBUG(glGetProgramiv(m_iProgram, GL_INFO_LOG_LENGTH, &logLength)); - if (logLength > 0) - { - GLchar *log = (GLchar *)malloc(logLength + 1); - assert(log != NULL); - log[0] = '\0'; // In case glGetProgramInfoLog fails - GLDEBUG(glGetProgramInfoLog(m_iProgram, logLength, &logLength, log)); - log[logLength] = '\0'; - KRContext::Log(KRContext::LOG_LEVEL_ERROR, "Program link log:\n%s", log); - free(log); - } - GLDEBUG(glDeleteProgram(m_iProgram)); - m_iProgram = 0; - } else { - - // Get uniform locations - for(int i=0; i < KRENGINE_NUM_UNIFORMS; i++ ){ - GLDEBUG(m_uniforms[i] = glGetUniformLocation(m_iProgram, KRENGINE_UNIFORM_NAMES[i])); - m_uniform_value_index[i] = -1; - } - } - - } catch(...) { - if(vertexShader) { - GLDEBUG(glDeleteShader(vertexShader)); - vertexShader = 0; - } - if(fragShader) { - GLDEBUG(glDeleteShader(fragShader)); - fragShader = 0; - } - if(m_iProgram) { - GLDEBUG(glDeleteProgram(m_iProgram)); - m_iProgram = 0; - } - } - - // Release vertex and fragment shaders. - if (vertexShader) { - GLDEBUG(glDeleteShader(vertexShader)); - } - if (fragShader) { - GLDEBUG(glDeleteShader(fragShader)); - } -} - -KRShader::~KRShader() { - if(m_iProgram) { - GLDEBUG(glDeleteProgram(m_iProgram)); - if(getContext().getShaderManager()->m_active_shader == this) { - getContext().getShaderManager()->m_active_shader = NULL; - } - } -} - -void KRShader::setUniform(int location, float value) -{ - if(m_uniforms[location] != -1) { - int value_index = m_uniform_value_index[location]; - bool needs_update = true; - if(value_index == -1) { - m_uniform_value_index[location] = m_uniform_value_float.size(); - m_uniform_value_float.push_back(value); - } else if(m_uniform_value_float[value_index] == value) { - needs_update = false; - } else { - m_uniform_value_float[value_index] = value; - } - if(needs_update) { - GLDEBUG(glUniform1f(m_uniforms[location], value)); - } - } -} -void KRShader::setUniform(int location, int value) -{ - if(m_uniforms[location] != -1) { - int value_index = m_uniform_value_index[location]; - bool needs_update = true; - if(value_index == -1) { - m_uniform_value_index[location] = m_uniform_value_int.size(); - m_uniform_value_int.push_back(value); - } else if(m_uniform_value_int[value_index] == value) { - needs_update = false; - } else { - m_uniform_value_int[value_index] = value; - } - if(needs_update) { - GLDEBUG(glUniform1i(m_uniforms[location], value)); - } - } -} - -void KRShader::setUniform(int location, const Vector2 &value) -{ - if(m_uniforms[location] != -1) { - int value_index = m_uniform_value_index[location]; - bool needs_update = true; - if(value_index == -1) { - m_uniform_value_index[location] = m_uniform_value_vector2.size(); - m_uniform_value_vector2.push_back(value); - } else if(m_uniform_value_vector2[value_index] == value) { - needs_update = false; - } else { - m_uniform_value_vector2[value_index] = value; - } - if(needs_update) { - GLDEBUG(glUniform2f(m_uniforms[location], value.x, value.y)); - } - } -} -void KRShader::setUniform(int location, const Vector3 &value) -{ - if(m_uniforms[location] != -1) { - int value_index = m_uniform_value_index[location]; - bool needs_update = true; - if(value_index == -1) { - m_uniform_value_index[location] = m_uniform_value_vector3.size(); - m_uniform_value_vector3.push_back(value); - } else if(m_uniform_value_vector3[value_index] == value) { - needs_update = false; - } else { - m_uniform_value_vector3[value_index] = value; - } - if(needs_update) { - GLDEBUG(glUniform3f(m_uniforms[location], value.x, value.y, value.z)); - } - } -} -void KRShader::setUniform(int location, const Vector4 &value) -{ - if(m_uniforms[location] != -1) { - int value_index = m_uniform_value_index[location]; - bool needs_update = true; - if(value_index == -1) { - m_uniform_value_index[location] = m_uniform_value_vector4.size(); - m_uniform_value_vector4.push_back(value); - } else if(m_uniform_value_vector4[value_index] == value) { - needs_update = false; - } else { - m_uniform_value_vector4[value_index] = value; - } - if(needs_update) { - GLDEBUG(glUniform4f(m_uniforms[location], value.x, value.y, value.z, value.w)); - } - } -} - -void KRShader::setUniform(int location, const Matrix4 &value) -{ - if(m_uniforms[location] != -1) { - int value_index = m_uniform_value_index[location]; - bool needs_update = true; - if(value_index == -1) { - m_uniform_value_index[location] = m_uniform_value_mat4.size(); - m_uniform_value_mat4.push_back(value); - } else if(m_uniform_value_mat4[value_index] == value) { - needs_update = false; - } else { - m_uniform_value_mat4[value_index] = value; - } - if(needs_update) { - GLDEBUG(glUniformMatrix4fv(m_uniforms[location], 1, GL_FALSE, value.c)); - } - } -} - -bool KRShader::bind(KRCamera &camera, const KRViewport &viewport, const Matrix4 &matModel, const std::vector &point_lights, const std::vector &directional_lights, const std::vector&spot_lights, const KRNode::RenderPass &renderPass, const Vector3 &rim_color, float rim_power, const Vector4 &fade_color) { - if(m_iProgram == 0) { - return false; - } - - bool shander_changed = false; - if(getContext().getShaderManager()->m_active_shader != this) { - getContext().getShaderManager()->m_active_shader = this; - GLDEBUG(glUseProgram(m_iProgram)); - shander_changed = true; - } - - - setUniform(KRENGINE_UNIFORM_ABSOLUTE_TIME, getContext().getAbsoluteTime()); - - int light_directional_count = 0; - //int light_point_count = 0; - //int light_spot_count = 0; - // TODO - Need to support multiple lights and more light types in forward rendering - if(renderPass != KRNode::RENDER_PASS_DEFERRED_LIGHTS && renderPass != KRNode::RENDER_PASS_DEFERRED_GBUFFER && renderPass != KRNode::RENDER_PASS_DEFERRED_OPAQUE && renderPass != KRNode::RENDER_PASS_GENERATE_SHADOWMAPS) { - - - for(std::vector::const_iterator light_itr=directional_lights.begin(); light_itr != directional_lights.end(); light_itr++) { - KRDirectionalLight *directional_light = (*light_itr); - if(light_directional_count == 0) { - int cShadowBuffers = directional_light->getShadowBufferCount(); - if(m_uniforms[KRENGINE_UNIFORM_SHADOWTEXTURE1] != -1 && cShadowBuffers > 0) { - if(m_pContext->getTextureManager()->selectTexture(GL_TEXTURE_2D, 3, directional_light->getShadowTextures()[0])) { - GLDEBUG(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); - GLDEBUG(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); - } - - m_pContext->getTextureManager()->_setWrapModeS(3, GL_CLAMP_TO_EDGE); - m_pContext->getTextureManager()->_setWrapModeT(3, GL_CLAMP_TO_EDGE); - } - - if(m_uniforms[KRENGINE_UNIFORM_SHADOWTEXTURE2] != -1 && cShadowBuffers > 1 && camera.settings.m_cShadowBuffers > 1) { - if(m_pContext->getTextureManager()->selectTexture(GL_TEXTURE_2D, 4, directional_light->getShadowTextures()[1])) { - GLDEBUG(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); - GLDEBUG(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); - } - m_pContext->getTextureManager()->_setWrapModeS(4, GL_CLAMP_TO_EDGE); - m_pContext->getTextureManager()->_setWrapModeT(4, GL_CLAMP_TO_EDGE); - } - - if(m_uniforms[KRENGINE_UNIFORM_SHADOWTEXTURE3] != -1 && cShadowBuffers > 2 && camera.settings.m_cShadowBuffers > 2) { - if(m_pContext->getTextureManager()->selectTexture(GL_TEXTURE_2D, 5, directional_light->getShadowTextures()[2])) { - GLDEBUG(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); - GLDEBUG(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); - } - m_pContext->getTextureManager()->_setWrapModeS(5, GL_CLAMP_TO_EDGE); - m_pContext->getTextureManager()->_setWrapModeT(5, GL_CLAMP_TO_EDGE); - } - - Matrix4 matBias; - matBias.translate(1.0, 1.0, 1.0); - matBias.scale(0.5); - for(int iShadow=0; iShadow < cShadowBuffers; iShadow++) { - setUniform(KRENGINE_UNIFORM_SHADOWMVP1 + iShadow, matModel * directional_light->getShadowViewports()[iShadow].getViewProjectionMatrix() * matBias); - } - - if(m_uniforms[KRENGINE_UNIFORM_LIGHT_DIRECTION_MODEL_SPACE] != -1) { - Matrix4 inverseModelMatrix = matModel; - inverseModelMatrix.invert(); - - // Bind the light direction vector - Vector3 lightDirObject = Matrix4::Dot(inverseModelMatrix, directional_light->getWorldLightDirection()); - lightDirObject.normalize(); - setUniform(KRENGINE_UNIFORM_LIGHT_DIRECTION_MODEL_SPACE, lightDirObject); - } - } - - light_directional_count++; - } - - //light_point_count = point_lights.size(); - //light_spot_count = spot_lights.size(); - } - - - - if(m_uniforms[KRENGINE_UNIFORM_CAMERAPOS_MODEL_SPACE] != -1) { - Matrix4 inverseModelMatrix = matModel; - inverseModelMatrix.invert(); - - if(m_uniforms[KRENGINE_UNIFORM_CAMERAPOS_MODEL_SPACE] != -1) { - // Transform location of camera to object space for calculation of specular halfVec - Vector3 cameraPosObject = Matrix4::Dot(inverseModelMatrix, viewport.getCameraPosition()); - setUniform(KRENGINE_UNIFORM_CAMERAPOS_MODEL_SPACE, cameraPosObject); - } - } - - if(m_uniforms[KRENGINE_UNIFORM_MVP] != -1 || m_uniforms[KRShader::KRENGINE_UNIFORM_INVMVP] != -1) { - // Bind our modelmatrix variable to be a uniform called mvpmatrix in our shaderprogram - Matrix4 mvpMatrix = matModel * viewport.getViewProjectionMatrix(); - setUniform(KRENGINE_UNIFORM_MVP, mvpMatrix); - - if(m_uniforms[KRShader::KRENGINE_UNIFORM_INVMVP] != -1) { - setUniform(KRShader::KRENGINE_UNIFORM_INVMVP, Matrix4::Invert(mvpMatrix)); - } - } - - if(m_uniforms[KRShader::KRENGINE_UNIFORM_VIEW_SPACE_MODEL_ORIGIN] != -1 || m_uniforms[KRENGINE_UNIFORM_MODEL_VIEW_INVERSE_TRANSPOSE] != -1 || m_uniforms[KRShader::KRENGINE_UNIFORM_MODEL_VIEW] != -1) { - Matrix4 matModelView = matModel * viewport.getViewMatrix(); - setUniform(KRENGINE_UNIFORM_MODEL_VIEW, matModelView); - - - if(m_uniforms[KRShader::KRENGINE_UNIFORM_VIEW_SPACE_MODEL_ORIGIN] != -1) { - Vector3 view_space_model_origin = Matrix4::Dot(matModelView, Vector3::Zero()); // Origin point of model space is the light source position. No perspective, so no w divide required - setUniform(KRENGINE_UNIFORM_VIEW_SPACE_MODEL_ORIGIN, view_space_model_origin); - } - - if(m_uniforms[KRENGINE_UNIFORM_MODEL_VIEW_INVERSE_TRANSPOSE] != -1) { - Matrix4 matModelViewInverseTranspose = matModelView; - matModelViewInverseTranspose.transpose(); - matModelViewInverseTranspose.invert(); - setUniform(KRENGINE_UNIFORM_MODEL_VIEW_INVERSE_TRANSPOSE, matModelViewInverseTranspose); - } - } - - if(m_uniforms[KRENGINE_UNIFORM_MODEL_INVERSE_TRANSPOSE] != -1) { - Matrix4 matModelInverseTranspose = matModel; - matModelInverseTranspose.transpose(); - matModelInverseTranspose.invert(); - setUniform(KRENGINE_UNIFORM_MODEL_INVERSE_TRANSPOSE, matModelInverseTranspose); - } - - if(m_uniforms[KRShader::KRENGINE_UNIFORM_INVP] != -1) { - setUniform(KRENGINE_UNIFORM_INVP, viewport.getInverseProjectionMatrix()); - } - - if(m_uniforms[KRShader::KRENGINE_UNIFORM_INVMVP_NO_TRANSLATE] != -1) { - Matrix4 matInvMVPNoTranslate = matModel * viewport.getViewMatrix();; - // Remove the translation - matInvMVPNoTranslate.getPointer()[3] = 0; - matInvMVPNoTranslate.getPointer()[7] = 0; - matInvMVPNoTranslate.getPointer()[11] = 0; - matInvMVPNoTranslate.getPointer()[12] = 0; - matInvMVPNoTranslate.getPointer()[13] = 0; - matInvMVPNoTranslate.getPointer()[14] = 0; - matInvMVPNoTranslate.getPointer()[15] = 1.0; - matInvMVPNoTranslate = matInvMVPNoTranslate * viewport.getProjectionMatrix(); - matInvMVPNoTranslate.invert(); - setUniform(KRENGINE_UNIFORM_INVMVP_NO_TRANSLATE, matInvMVPNoTranslate); - } - - setUniform(KRENGINE_UNIFORM_MODEL_MATRIX, matModel); - if(m_uniforms[KRENGINE_UNIFORM_PROJECTION_MATRIX] != -1) { - setUniform(KRENGINE_UNIFORM_PROJECTION_MATRIX, viewport.getProjectionMatrix()); - } - - if(m_uniforms[KRENGINE_UNIFORM_VIEWPORT] != -1) { - setUniform(KRENGINE_UNIFORM_VIEWPORT, Vector4( - (GLfloat)0.0, - (GLfloat)0.0, - (GLfloat)viewport.getSize().x, - (GLfloat)viewport.getSize().y - ) - ); - } - - if(m_uniforms[KRENGINE_UNIFORM_VIEWPORT_DOWNSAMPLE] != -1) { - setUniform(KRENGINE_UNIFORM_VIEWPORT_DOWNSAMPLE, camera.getDownsample()); - } - - // Rim highlighting parameters - setUniform(KRENGINE_UNIFORM_RIM_COLOR, rim_color); - setUniform(KRENGINE_UNIFORM_RIM_POWER, rim_power); - - // Fade parameters - setUniform(KRENGINE_UNIFORM_FADE_COLOR, fade_color); - - // Fog parameters - setUniform(KRENGINE_UNIFORM_FOG_NEAR, camera.settings.fog_near); - setUniform(KRENGINE_UNIFORM_FOG_FAR, camera.settings.fog_far); - setUniform(KRENGINE_UNIFORM_FOG_DENSITY, camera.settings.fog_density); - setUniform(KRENGINE_UNIFORM_FOG_COLOR, camera.settings.fog_color); - - if(m_uniforms[KRENGINE_UNIFORM_FOG_SCALE] != -1) { - setUniform(KRENGINE_UNIFORM_FOG_SCALE, 1.0f / (camera.settings.fog_far - camera.settings.fog_near)); - } - if(m_uniforms[KRENGINE_UNIFORM_DENSITY_PREMULTIPLIED_EXPONENTIAL] != -1) { - setUniform(KRENGINE_UNIFORM_DENSITY_PREMULTIPLIED_EXPONENTIAL, -camera.settings.fog_density * 1.442695f); // -fog_density / log(2) - } - if(m_uniforms[KRENGINE_UNIFORM_DENSITY_PREMULTIPLIED_SQUARED] != -1) { - setUniform(KRENGINE_UNIFORM_DENSITY_PREMULTIPLIED_SQUARED, (float)(-camera.settings.fog_density * camera.settings.fog_density * 1.442695)); // -fog_density * fog_density / log(2) - } - - // Sets the diffuseTexture variable to the first texture unit - setUniform(KRENGINE_UNIFORM_DIFFUSETEXTURE, 0); - - // Sets the specularTexture variable to the second texture unit - setUniform(KRENGINE_UNIFORM_SPECULARTEXTURE, 1); - - // Sets the normalTexture variable to the third texture unit - setUniform(KRENGINE_UNIFORM_NORMALTEXTURE, 2); - - // Sets the shadowTexture variable to the fourth texture unit - setUniform(KRENGINE_UNIFORM_SHADOWTEXTURE1, 3); - setUniform(KRENGINE_UNIFORM_SHADOWTEXTURE2, 4); - setUniform(KRENGINE_UNIFORM_SHADOWTEXTURE3, 5); - setUniform(KRENGINE_UNIFORM_REFLECTIONCUBETEXTURE, 4); - setUniform(KRENGINE_UNIFORM_LIGHTMAPTEXTURE, 5); - setUniform(KRENGINE_UNIFORM_GBUFFER_FRAME, 6); - setUniform(KRENGINE_UNIFORM_GBUFFER_DEPTH, 7); // Texture unit 7 is used for reading the depth buffer in gBuffer pass #2 and in post-processing pass - setUniform(KRENGINE_UNIFORM_REFLECTIONTEXTURE, 7); // Texture unit 7 is used for the reflection map textures in gBuffer pass #3 and when using forward rendering - setUniform(KRENGINE_UNIFORM_DEPTH_FRAME, 0); - setUniform(KRENGINE_UNIFORM_RENDER_FRAME, 1); - setUniform(KRENGINE_UNIFORM_VOLUMETRIC_ENVIRONMENT_FRAME, 2); - -#if defined(DEBUG) - if(shander_changed) { // FINDME!! KIP!! HACK!! - GLint logLength; - - GLint validate_status = GL_FALSE; - GLDEBUG(glValidateProgram(m_iProgram)); - GLDEBUG(glGetProgramiv(m_iProgram, GL_VALIDATE_STATUS, &validate_status)); - if(validate_status != GL_TRUE) { - KRContext::Log(KRContext::LOG_LEVEL_ERROR, "KREngine - Failed to validate shader program: %s", m_szKey); - logLength = 0; // In case glGetProgramiv fails - GLDEBUG(glGetProgramiv(m_iProgram, GL_INFO_LOG_LENGTH, &logLength)); - if (logLength > 0) - { - GLchar *log = (GLchar *)malloc(logLength + 1); - assert(log != NULL); - log[0] = '\0'; // In case glGetProgramInfoLog fails - GLDEBUG(glGetProgramInfoLog(m_iProgram, logLength, &logLength, log)); - log[logLength] = '\0'; - KRContext::Log(KRContext::LOG_LEVEL_ERROR, "Program validate log:\n%s", log); - free(log); - - } - return false; - } - } -#endif - - return true; -} - -const char *KRShader::getKey() const { - return m_szKey; -} +// +// KRShader.cpp +// KREngine +// +// Copyright 2012 Kearwood Gilbert. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other materials +// provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// The views and conclusions contained in the software and documentation are those of the +// authors and should not be interpreted as representing official policies, either expressed +// or implied, of Kearwood Gilbert. +// + +#include "KRShader.h" +#include "assert.h" +#include "KRLight.h" +#include "KRDirectionalLight.h" +#include "KRSpotLight.h" +#include "KRPointLight.h" + + +const char *KRShader::KRENGINE_UNIFORM_NAMES[] = { + "material_ambient", // KRENGINE_UNIFORM_MATERIAL_AMBIENT + "material_diffuse", // KRENGINE_UNIFORM_MATERIAL_DIFFUSE + "material_specular", // KRENGINE_UNIFORM_MATERIAL_SPECULAR + "material_reflection", // KRENGINE_UNIFORM_MATERIAL_REFLECTION + "material_alpha", // KRENGINE_UNIFORM_MATERIAL_ALPHA + "material_shininess", // KRENGINE_UNIFORM_MATERIAL_SHININESS + "light_position", // KRENGINE_UNIFORM_LIGHT_POSITION + "light_direction_model_space", // KRENGINE_UNIFORM_LIGHT_DIRECTION_MODEL_SPACE + "light_direction_view_space", // KRENGINE_UNIFORM_LIGHT_DIRECTION_VIEW_SPACE + "light_color", // KRENGINE_UNIFORM_LIGHT_COLOR + "light_decay_start", // KRENGINE_UNIFORM_LIGHT_DECAY_START + "light_cutoff", // KRENGINE_UNIFORM_LIGHT_CUTOFF + "light_intensity", // KRENGINE_UNIFORM_LIGHT_INTENSITY + "flare_size", // KRENGINE_UNIFORM_FLARE_SIZE + "view_space_model_origin", // KRENGINE_UNIFORM_VIEW_SPACE_MODEL_ORIGIN + "mvp_matrix", // KRENGINE_UNIFORM_MVP + "inv_projection_matrix", // KRENGINE_UNIFORM_INVP + "inv_mvp_matrix", // KRENGINE_UNIFORM_INVMVP + "inv_mvp_matrix_no_translate", // KRENGINE_UNIFORM_INVMVP_NO_TRANSLATE + "model_view_inverse_transpose_matrix", // KRENGINE_UNIFORM_MODEL_VIEW_INVERSE_TRANSPOSE + "model_inverse_transpose_matrix", // KRENGINE_UNIFORM_MODEL_INVERSE_TRANSPOSE + "model_view_matrix", // KRENGINE_UNIFORM_MODEL_VIEW + "model_matrix", // KRENGINE_UNIFORM_MODEL_MATRIX + "projection_matrix", // KRENGINE_UNIFORM_PROJECTION_MATRIX + "camera_position_model_space", // KRENGINE_UNIFORM_CAMERAPOS_MODEL_SPACE + "viewport", // KRENGINE_UNIFORM_VIEWPORT + "viewport_downsample", // KRENGINE_UNIFORM_VIEWPORT_DOWNSAMPLE + "diffuseTexture", // KRENGINE_UNIFORM_DIFFUSETEXTURE + "specularTexture", // KRENGINE_UNIFORM_SPECULARTEXTURE + "reflectionCubeTexture", // KRENGINE_UNIFORM_REFLECTIONCUBETEXTURE + "reflectionTexture", // KRENGINE_UNIFORM_REFLECTIONTEXTURE + "normalTexture", // KRENGINE_UNIFORM_NORMALTEXTURE + "diffuseTexture_Scale", // KRENGINE_UNIFORM_DIFFUSETEXTURE_SCALE + "specularTexture_Scale", // KRENGINE_UNIFORM_SPECULARTEXTURE_SCALE + "reflectionTexture_Scale", // KRENGINE_UNIFORM_REFLECTIONTEXTURE_SCALE + "normalTexture_Scale", // KRENGINE_UNIFORM_NORMALTEXTURE_SCALE + "normalTexture_Scale", // KRENGINE_UNIFORM_AMBIENTTEXTURE_SCALE + "diffuseTexture_Offset", // KRENGINE_UNIFORM_DIFFUSETEXTURE_OFFSET + "specularTexture_Offset", // KRENGINE_UNIFORM_SPECULARTEXTURE_OFFSET + "reflectionTexture_Offset", // KRENGINE_UNIFORM_REFLECTIONTEXTURE_OFFSET + "normalTexture_Offset", // KRENGINE_UNIFORM_NORMALTEXTURE_OFFSET + "ambientTexture_Offset", // KRENGINE_UNIFORM_AMBIENTTEXTURE_OFFSET + "shadow_mvp1", // KRENGINE_UNIFORM_SHADOWMVP1 + "shadow_mvp2", // KRENGINE_UNIFORM_SHADOWMVP2 + "shadow_mvp3", // KRENGINE_UNIFORM_SHADOWMVP3 + "shadowTexture1", // KRENGINE_UNIFORM_SHADOWTEXTURE1 + "shadowTexture2", // KRENGINE_UNIFORM_SHADOWTEXTURE2 + "shadowTexture3", // KRENGINE_UNIFORM_SHADOWTEXTURE3 + "lightmapTexture", // KRENGINE_UNIFORM_LIGHTMAPTEXTURE + "gbuffer_frame", // KRENGINE_UNIFORM_GBUFFER_FRAME + "gbuffer_depth", // KRENGINE_UNIFORM_GBUFFER_DEPTH + "depthFrame", // KRENGINE_UNIFORM_DEPTH_FRAME + "volumetricEnvironmentFrame", // KRENGINE_UNIFORM_VOLUMETRIC_ENVIRONMENT_FRAME + "renderFrame", // KRENGINE_UNIFORM_RENDER_FRAME + "time_absolute", // KRENGINE_UNIFORM_ABSOLUTE_TIME + "fog_near", // KRENGINE_UNIFORM_FOG_NEAR + "fog_far", // KRENGINE_UNIFORM_FOG_FAR + "fog_density", // KRENGINE_UNIFORM_FOG_DENSITY + "fog_color", // KRENGINE_UNIFORM_FOG_COLOR + "fog_scale", // KRENGINE_UNIFORM_FOG_SCALE + "fog_density_premultiplied_exponential", // KRENGINE_UNIFORM_DENSITY_PREMULTIPLIED_EXPONENTIAL + "fog_density_premultiplied_squared", // KRENGINE_UNIFORM_DENSITY_PREMULTIPLIED_SQUARED + "slice_depth_scale", // KRENGINE_UNIFORM_SLICE_DEPTH_SCALE + "particle_origin", // KRENGINE_UNIFORM_PARTICLE_ORIGIN + "bone_transforms", // KRENGINE_UNIFORM_BONE_TRANSFORMS + "rim_color", // KRENGINE_UNIFORM_RIM_COLOR + "rim_power", // KRENGINE_UNIFORM_RIM_POWER + "fade_color", // KRENGINE_UNIFORM_FADE_COLOR +}; + +KRShader::KRShader(KRContext &context, char *szKey, std::string options, std::string vertShaderSource, const std::string fragShaderSource) : KRContextObject(context) +{ + strcpy(m_szKey, szKey); + m_iProgram = 0; + + + GLuint vertexShader = 0, fragShader = 0; + try { + const GLchar *vertSource[2] = {options.c_str(), vertShaderSource.c_str()}; + const GLchar *fragSource[2] = {options.c_str(), fragShaderSource.c_str()}; + + // Create shader program. + GLDEBUG(m_iProgram = glCreateProgram()); + + // Create and compile vertex shader. + GLDEBUG(vertexShader = glCreateShader(GL_VERTEX_SHADER)); + GLDEBUG(glShaderSource(vertexShader, 2, vertSource, NULL)); + GLDEBUG(glCompileShader(vertexShader)); + + // Report any compile issues to stderr + GLint logLength = 0; + GLDEBUG(glGetShaderiv(vertexShader, GL_INFO_LOG_LENGTH, &logLength)); + if (logLength > 0) { + GLchar *log = (GLchar *)malloc(logLength + 1); + assert(log != NULL); + log[0] = '\0'; // In case glGetShaderInfoLog fails + GLDEBUG(glGetShaderInfoLog(vertexShader, logLength, &logLength, log)); + log[logLength] = '\0'; + KRContext::Log(KRContext::LOG_LEVEL_ERROR, "KREngine - Failed to compile vertex shader: %s\nShader compile log:\n%s", szKey, log); + free(log); + } + + + // Create and compile vertex shader. + GLDEBUG(fragShader = glCreateShader(GL_FRAGMENT_SHADER)); + GLDEBUG(glShaderSource(fragShader, 2, fragSource, NULL)); + GLDEBUG(glCompileShader(fragShader)); + + // Report any compile issues to stderr + logLength = 0; // In case glGetShaderiv fails + GLDEBUG(glGetShaderiv(fragShader, GL_INFO_LOG_LENGTH, &logLength)); + if (logLength > 0) { + GLchar *log = (GLchar *)malloc(logLength + 1); + assert(log != NULL); + log[0] = '\0'; // In case glGetShaderInfoLog fails + GLDEBUG(glGetShaderInfoLog(fragShader, logLength, &logLength, log)); + log[logLength] = '\0'; + KRContext::Log(KRContext::LOG_LEVEL_ERROR, "KREngine - Failed to compile fragment shader: %s\nShader compile log:\n%s", szKey, log); + free(log); + } + + // Attach vertex shader to program. + GLDEBUG(glAttachShader(m_iProgram, vertexShader)); + + // Attach fragment shader to program. + GLDEBUG(glAttachShader(m_iProgram, fragShader)); + + // Bind attribute locations. + // This needs to be done prior to linking. + GLDEBUG(glBindAttribLocation(m_iProgram, KRMesh::KRENGINE_ATTRIB_VERTEX, "vertex_position")); + GLDEBUG(glBindAttribLocation(m_iProgram, KRMesh::KRENGINE_ATTRIB_NORMAL, "vertex_normal")); + GLDEBUG(glBindAttribLocation(m_iProgram, KRMesh::KRENGINE_ATTRIB_TANGENT, "vertex_tangent")); + GLDEBUG(glBindAttribLocation(m_iProgram, KRMesh::KRENGINE_ATTRIB_TEXUVA, "vertex_uv")); + GLDEBUG(glBindAttribLocation(m_iProgram, KRMesh::KRENGINE_ATTRIB_TEXUVB, "vertex_lightmap_uv")); + GLDEBUG(glBindAttribLocation(m_iProgram, KRMesh::KRENGINE_ATTRIB_BONEINDEXES, "bone_indexes")); + GLDEBUG(glBindAttribLocation(m_iProgram, KRMesh::KRENGINE_ATTRIB_BONEWEIGHTS, "bone_weights")); + + // Link program. + GLDEBUG(glLinkProgram(m_iProgram)); + + GLint link_success = GL_FALSE; + GLDEBUG(glGetProgramiv(m_iProgram, GL_LINK_STATUS, &link_success)); + + if(link_success != GL_TRUE) { + // Report any linking issues to stderr + KRContext::Log(KRContext::LOG_LEVEL_ERROR, "KREngine - Failed to link shader program: %s", szKey); + logLength = 0; // In case glGetProgramiv fails + GLDEBUG(glGetProgramiv(m_iProgram, GL_INFO_LOG_LENGTH, &logLength)); + if (logLength > 0) + { + GLchar *log = (GLchar *)malloc(logLength + 1); + assert(log != NULL); + log[0] = '\0'; // In case glGetProgramInfoLog fails + GLDEBUG(glGetProgramInfoLog(m_iProgram, logLength, &logLength, log)); + log[logLength] = '\0'; + KRContext::Log(KRContext::LOG_LEVEL_ERROR, "Program link log:\n%s", log); + free(log); + } + GLDEBUG(glDeleteProgram(m_iProgram)); + m_iProgram = 0; + } else { + + // Get uniform locations + for(int i=0; i < KRENGINE_NUM_UNIFORMS; i++ ){ + GLDEBUG(m_uniforms[i] = glGetUniformLocation(m_iProgram, KRENGINE_UNIFORM_NAMES[i])); + m_uniform_value_index[i] = -1; + } + } + + } catch(...) { + if(vertexShader) { + GLDEBUG(glDeleteShader(vertexShader)); + vertexShader = 0; + } + if(fragShader) { + GLDEBUG(glDeleteShader(fragShader)); + fragShader = 0; + } + if(m_iProgram) { + GLDEBUG(glDeleteProgram(m_iProgram)); + m_iProgram = 0; + } + } + + // Release vertex and fragment shaders. + if (vertexShader) { + GLDEBUG(glDeleteShader(vertexShader)); + } + if (fragShader) { + GLDEBUG(glDeleteShader(fragShader)); + } +} + +KRShader::~KRShader() { + if(m_iProgram) { + GLDEBUG(glDeleteProgram(m_iProgram)); + if(getContext().getShaderManager()->m_active_shader == this) { + getContext().getShaderManager()->m_active_shader = NULL; + } + } +} + +void KRShader::setUniform(int location, float value) +{ + if(m_uniforms[location] != -1) { + int value_index = m_uniform_value_index[location]; + bool needs_update = true; + if(value_index == -1) { + m_uniform_value_index[location] = m_uniform_value_float.size(); + m_uniform_value_float.push_back(value); + } else if(m_uniform_value_float[value_index] == value) { + needs_update = false; + } else { + m_uniform_value_float[value_index] = value; + } + if(needs_update) { + GLDEBUG(glUniform1f(m_uniforms[location], value)); + } + } +} +void KRShader::setUniform(int location, int value) +{ + if(m_uniforms[location] != -1) { + int value_index = m_uniform_value_index[location]; + bool needs_update = true; + if(value_index == -1) { + m_uniform_value_index[location] = m_uniform_value_int.size(); + m_uniform_value_int.push_back(value); + } else if(m_uniform_value_int[value_index] == value) { + needs_update = false; + } else { + m_uniform_value_int[value_index] = value; + } + if(needs_update) { + GLDEBUG(glUniform1i(m_uniforms[location], value)); + } + } +} + +void KRShader::setUniform(int location, const Vector2 &value) +{ + if(m_uniforms[location] != -1) { + int value_index = m_uniform_value_index[location]; + bool needs_update = true; + if(value_index == -1) { + m_uniform_value_index[location] = m_uniform_value_vector2.size(); + m_uniform_value_vector2.push_back(value); + } else if(m_uniform_value_vector2[value_index] == value) { + needs_update = false; + } else { + m_uniform_value_vector2[value_index] = value; + } + if(needs_update) { + GLDEBUG(glUniform2f(m_uniforms[location], value.x, value.y)); + } + } +} +void KRShader::setUniform(int location, const Vector3 &value) +{ + if(m_uniforms[location] != -1) { + int value_index = m_uniform_value_index[location]; + bool needs_update = true; + if(value_index == -1) { + m_uniform_value_index[location] = m_uniform_value_vector3.size(); + m_uniform_value_vector3.push_back(value); + } else if(m_uniform_value_vector3[value_index] == value) { + needs_update = false; + } else { + m_uniform_value_vector3[value_index] = value; + } + if(needs_update) { + GLDEBUG(glUniform3f(m_uniforms[location], value.x, value.y, value.z)); + } + } +} +void KRShader::setUniform(int location, const Vector4 &value) +{ + if(m_uniforms[location] != -1) { + int value_index = m_uniform_value_index[location]; + bool needs_update = true; + if(value_index == -1) { + m_uniform_value_index[location] = m_uniform_value_vector4.size(); + m_uniform_value_vector4.push_back(value); + } else if(m_uniform_value_vector4[value_index] == value) { + needs_update = false; + } else { + m_uniform_value_vector4[value_index] = value; + } + if(needs_update) { + GLDEBUG(glUniform4f(m_uniforms[location], value.x, value.y, value.z, value.w)); + } + } +} + +void KRShader::setUniform(int location, const Matrix4 &value) +{ + if(m_uniforms[location] != -1) { + int value_index = m_uniform_value_index[location]; + bool needs_update = true; + if(value_index == -1) { + m_uniform_value_index[location] = m_uniform_value_mat4.size(); + m_uniform_value_mat4.push_back(value); + } else if(m_uniform_value_mat4[value_index] == value) { + needs_update = false; + } else { + m_uniform_value_mat4[value_index] = value; + } + if(needs_update) { + GLDEBUG(glUniformMatrix4fv(m_uniforms[location], 1, GL_FALSE, value.c)); + } + } +} + +bool KRShader::bind(KRCamera &camera, const KRViewport &viewport, const Matrix4 &matModel, const std::vector &point_lights, const std::vector &directional_lights, const std::vector&spot_lights, const KRNode::RenderPass &renderPass, const Vector3 &rim_color, float rim_power, const Vector4 &fade_color) { + if(m_iProgram == 0) { + return false; + } + + bool shander_changed = false; + if(getContext().getShaderManager()->m_active_shader != this) { + getContext().getShaderManager()->m_active_shader = this; + GLDEBUG(glUseProgram(m_iProgram)); + shander_changed = true; + } + + + setUniform(KRENGINE_UNIFORM_ABSOLUTE_TIME, getContext().getAbsoluteTime()); + + int light_directional_count = 0; + //int light_point_count = 0; + //int light_spot_count = 0; + // TODO - Need to support multiple lights and more light types in forward rendering + if(renderPass != KRNode::RENDER_PASS_DEFERRED_LIGHTS && renderPass != KRNode::RENDER_PASS_DEFERRED_GBUFFER && renderPass != KRNode::RENDER_PASS_DEFERRED_OPAQUE && renderPass != KRNode::RENDER_PASS_GENERATE_SHADOWMAPS) { + + + for(std::vector::const_iterator light_itr=directional_lights.begin(); light_itr != directional_lights.end(); light_itr++) { + KRDirectionalLight *directional_light = (*light_itr); + if(light_directional_count == 0) { + int cShadowBuffers = directional_light->getShadowBufferCount(); + if(m_uniforms[KRENGINE_UNIFORM_SHADOWTEXTURE1] != -1 && cShadowBuffers > 0) { + if(m_pContext->getTextureManager()->selectTexture(GL_TEXTURE_2D, 3, directional_light->getShadowTextures()[0])) { + GLDEBUG(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); + GLDEBUG(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); + } + + m_pContext->getTextureManager()->_setWrapModeS(3, GL_CLAMP_TO_EDGE); + m_pContext->getTextureManager()->_setWrapModeT(3, GL_CLAMP_TO_EDGE); + } + + if(m_uniforms[KRENGINE_UNIFORM_SHADOWTEXTURE2] != -1 && cShadowBuffers > 1 && camera.settings.m_cShadowBuffers > 1) { + if(m_pContext->getTextureManager()->selectTexture(GL_TEXTURE_2D, 4, directional_light->getShadowTextures()[1])) { + GLDEBUG(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); + GLDEBUG(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); + } + m_pContext->getTextureManager()->_setWrapModeS(4, GL_CLAMP_TO_EDGE); + m_pContext->getTextureManager()->_setWrapModeT(4, GL_CLAMP_TO_EDGE); + } + + if(m_uniforms[KRENGINE_UNIFORM_SHADOWTEXTURE3] != -1 && cShadowBuffers > 2 && camera.settings.m_cShadowBuffers > 2) { + if(m_pContext->getTextureManager()->selectTexture(GL_TEXTURE_2D, 5, directional_light->getShadowTextures()[2])) { + GLDEBUG(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); + GLDEBUG(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); + } + m_pContext->getTextureManager()->_setWrapModeS(5, GL_CLAMP_TO_EDGE); + m_pContext->getTextureManager()->_setWrapModeT(5, GL_CLAMP_TO_EDGE); + } + + Matrix4 matBias; + matBias.translate(1.0, 1.0, 1.0); + matBias.scale(0.5); + for(int iShadow=0; iShadow < cShadowBuffers; iShadow++) { + setUniform(KRENGINE_UNIFORM_SHADOWMVP1 + iShadow, matModel * directional_light->getShadowViewports()[iShadow].getViewProjectionMatrix() * matBias); + } + + if(m_uniforms[KRENGINE_UNIFORM_LIGHT_DIRECTION_MODEL_SPACE] != -1) { + Matrix4 inverseModelMatrix = matModel; + inverseModelMatrix.invert(); + + // Bind the light direction vector + Vector3 lightDirObject = Matrix4::Dot(inverseModelMatrix, directional_light->getWorldLightDirection()); + lightDirObject.normalize(); + setUniform(KRENGINE_UNIFORM_LIGHT_DIRECTION_MODEL_SPACE, lightDirObject); + } + } + + light_directional_count++; + } + + //light_point_count = point_lights.size(); + //light_spot_count = spot_lights.size(); + } + + + + if(m_uniforms[KRENGINE_UNIFORM_CAMERAPOS_MODEL_SPACE] != -1) { + Matrix4 inverseModelMatrix = matModel; + inverseModelMatrix.invert(); + + if(m_uniforms[KRENGINE_UNIFORM_CAMERAPOS_MODEL_SPACE] != -1) { + // Transform location of camera to object space for calculation of specular halfVec + Vector3 cameraPosObject = Matrix4::Dot(inverseModelMatrix, viewport.getCameraPosition()); + setUniform(KRENGINE_UNIFORM_CAMERAPOS_MODEL_SPACE, cameraPosObject); + } + } + + if(m_uniforms[KRENGINE_UNIFORM_MVP] != -1 || m_uniforms[KRShader::KRENGINE_UNIFORM_INVMVP] != -1) { + // Bind our modelmatrix variable to be a uniform called mvpmatrix in our shaderprogram + Matrix4 mvpMatrix = matModel * viewport.getViewProjectionMatrix(); + setUniform(KRENGINE_UNIFORM_MVP, mvpMatrix); + + if(m_uniforms[KRShader::KRENGINE_UNIFORM_INVMVP] != -1) { + setUniform(KRShader::KRENGINE_UNIFORM_INVMVP, Matrix4::Invert(mvpMatrix)); + } + } + + if(m_uniforms[KRShader::KRENGINE_UNIFORM_VIEW_SPACE_MODEL_ORIGIN] != -1 || m_uniforms[KRENGINE_UNIFORM_MODEL_VIEW_INVERSE_TRANSPOSE] != -1 || m_uniforms[KRShader::KRENGINE_UNIFORM_MODEL_VIEW] != -1) { + Matrix4 matModelView = matModel * viewport.getViewMatrix(); + setUniform(KRENGINE_UNIFORM_MODEL_VIEW, matModelView); + + + if(m_uniforms[KRShader::KRENGINE_UNIFORM_VIEW_SPACE_MODEL_ORIGIN] != -1) { + Vector3 view_space_model_origin = Matrix4::Dot(matModelView, Vector3::Zero()); // Origin point of model space is the light source position. No perspective, so no w divide required + setUniform(KRENGINE_UNIFORM_VIEW_SPACE_MODEL_ORIGIN, view_space_model_origin); + } + + if(m_uniforms[KRENGINE_UNIFORM_MODEL_VIEW_INVERSE_TRANSPOSE] != -1) { + Matrix4 matModelViewInverseTranspose = matModelView; + matModelViewInverseTranspose.transpose(); + matModelViewInverseTranspose.invert(); + setUniform(KRENGINE_UNIFORM_MODEL_VIEW_INVERSE_TRANSPOSE, matModelViewInverseTranspose); + } + } + + if(m_uniforms[KRENGINE_UNIFORM_MODEL_INVERSE_TRANSPOSE] != -1) { + Matrix4 matModelInverseTranspose = matModel; + matModelInverseTranspose.transpose(); + matModelInverseTranspose.invert(); + setUniform(KRENGINE_UNIFORM_MODEL_INVERSE_TRANSPOSE, matModelInverseTranspose); + } + + if(m_uniforms[KRShader::KRENGINE_UNIFORM_INVP] != -1) { + setUniform(KRENGINE_UNIFORM_INVP, viewport.getInverseProjectionMatrix()); + } + + if(m_uniforms[KRShader::KRENGINE_UNIFORM_INVMVP_NO_TRANSLATE] != -1) { + Matrix4 matInvMVPNoTranslate = matModel * viewport.getViewMatrix();; + // Remove the translation + matInvMVPNoTranslate.getPointer()[3] = 0; + matInvMVPNoTranslate.getPointer()[7] = 0; + matInvMVPNoTranslate.getPointer()[11] = 0; + matInvMVPNoTranslate.getPointer()[12] = 0; + matInvMVPNoTranslate.getPointer()[13] = 0; + matInvMVPNoTranslate.getPointer()[14] = 0; + matInvMVPNoTranslate.getPointer()[15] = 1.0; + matInvMVPNoTranslate = matInvMVPNoTranslate * viewport.getProjectionMatrix(); + matInvMVPNoTranslate.invert(); + setUniform(KRENGINE_UNIFORM_INVMVP_NO_TRANSLATE, matInvMVPNoTranslate); + } + + setUniform(KRENGINE_UNIFORM_MODEL_MATRIX, matModel); + if(m_uniforms[KRENGINE_UNIFORM_PROJECTION_MATRIX] != -1) { + setUniform(KRENGINE_UNIFORM_PROJECTION_MATRIX, viewport.getProjectionMatrix()); + } + + if(m_uniforms[KRENGINE_UNIFORM_VIEWPORT] != -1) { + setUniform(KRENGINE_UNIFORM_VIEWPORT, Vector4::Create( + (GLfloat)0.0, + (GLfloat)0.0, + (GLfloat)viewport.getSize().x, + (GLfloat)viewport.getSize().y + ) + ); + } + + if(m_uniforms[KRENGINE_UNIFORM_VIEWPORT_DOWNSAMPLE] != -1) { + setUniform(KRENGINE_UNIFORM_VIEWPORT_DOWNSAMPLE, camera.getDownsample()); + } + + // Rim highlighting parameters + setUniform(KRENGINE_UNIFORM_RIM_COLOR, rim_color); + setUniform(KRENGINE_UNIFORM_RIM_POWER, rim_power); + + // Fade parameters + setUniform(KRENGINE_UNIFORM_FADE_COLOR, fade_color); + + // Fog parameters + setUniform(KRENGINE_UNIFORM_FOG_NEAR, camera.settings.fog_near); + setUniform(KRENGINE_UNIFORM_FOG_FAR, camera.settings.fog_far); + setUniform(KRENGINE_UNIFORM_FOG_DENSITY, camera.settings.fog_density); + setUniform(KRENGINE_UNIFORM_FOG_COLOR, camera.settings.fog_color); + + if(m_uniforms[KRENGINE_UNIFORM_FOG_SCALE] != -1) { + setUniform(KRENGINE_UNIFORM_FOG_SCALE, 1.0f / (camera.settings.fog_far - camera.settings.fog_near)); + } + if(m_uniforms[KRENGINE_UNIFORM_DENSITY_PREMULTIPLIED_EXPONENTIAL] != -1) { + setUniform(KRENGINE_UNIFORM_DENSITY_PREMULTIPLIED_EXPONENTIAL, -camera.settings.fog_density * 1.442695f); // -fog_density / log(2) + } + if(m_uniforms[KRENGINE_UNIFORM_DENSITY_PREMULTIPLIED_SQUARED] != -1) { + setUniform(KRENGINE_UNIFORM_DENSITY_PREMULTIPLIED_SQUARED, (float)(-camera.settings.fog_density * camera.settings.fog_density * 1.442695)); // -fog_density * fog_density / log(2) + } + + // Sets the diffuseTexture variable to the first texture unit + setUniform(KRENGINE_UNIFORM_DIFFUSETEXTURE, 0); + + // Sets the specularTexture variable to the second texture unit + setUniform(KRENGINE_UNIFORM_SPECULARTEXTURE, 1); + + // Sets the normalTexture variable to the third texture unit + setUniform(KRENGINE_UNIFORM_NORMALTEXTURE, 2); + + // Sets the shadowTexture variable to the fourth texture unit + setUniform(KRENGINE_UNIFORM_SHADOWTEXTURE1, 3); + setUniform(KRENGINE_UNIFORM_SHADOWTEXTURE2, 4); + setUniform(KRENGINE_UNIFORM_SHADOWTEXTURE3, 5); + setUniform(KRENGINE_UNIFORM_REFLECTIONCUBETEXTURE, 4); + setUniform(KRENGINE_UNIFORM_LIGHTMAPTEXTURE, 5); + setUniform(KRENGINE_UNIFORM_GBUFFER_FRAME, 6); + setUniform(KRENGINE_UNIFORM_GBUFFER_DEPTH, 7); // Texture unit 7 is used for reading the depth buffer in gBuffer pass #2 and in post-processing pass + setUniform(KRENGINE_UNIFORM_REFLECTIONTEXTURE, 7); // Texture unit 7 is used for the reflection map textures in gBuffer pass #3 and when using forward rendering + setUniform(KRENGINE_UNIFORM_DEPTH_FRAME, 0); + setUniform(KRENGINE_UNIFORM_RENDER_FRAME, 1); + setUniform(KRENGINE_UNIFORM_VOLUMETRIC_ENVIRONMENT_FRAME, 2); + +#if defined(DEBUG) + if(shander_changed) { // FINDME!! KIP!! HACK!! + GLint logLength; + + GLint validate_status = GL_FALSE; + GLDEBUG(glValidateProgram(m_iProgram)); + GLDEBUG(glGetProgramiv(m_iProgram, GL_VALIDATE_STATUS, &validate_status)); + if(validate_status != GL_TRUE) { + KRContext::Log(KRContext::LOG_LEVEL_ERROR, "KREngine - Failed to validate shader program: %s", m_szKey); + logLength = 0; // In case glGetProgramiv fails + GLDEBUG(glGetProgramiv(m_iProgram, GL_INFO_LOG_LENGTH, &logLength)); + if (logLength > 0) + { + GLchar *log = (GLchar *)malloc(logLength + 1); + assert(log != NULL); + log[0] = '\0'; // In case glGetProgramInfoLog fails + GLDEBUG(glGetProgramInfoLog(m_iProgram, logLength, &logLength, log)); + log[logLength] = '\0'; + KRContext::Log(KRContext::LOG_LEVEL_ERROR, "Program validate log:\n%s", log); + free(log); + + } + return false; + } + } +#endif + + return true; +} + +const char *KRShader::getKey() const { + return m_szKey; +} diff --git a/kraken/KRSpotLight.cpp b/kraken/KRSpotLight.cpp index 5724b29..353214f 100755 --- a/kraken/KRSpotLight.cpp +++ b/kraken/KRSpotLight.cpp @@ -1,61 +1,61 @@ -// -// KRSpotLight.cpp -// KREngine -// -// Created by Kearwood Gilbert on 12-04-05. -// Copyright (c) 2012 Kearwood Software. All rights reserved. -// - -#include "KREngine-common.h" -#include "KRSpotLight.h" - -KRSpotLight::KRSpotLight(KRScene &scene, std::string name) : KRLight(scene, name) -{ -} - -KRSpotLight::~KRSpotLight() -{ - -} - -std::string KRSpotLight::getElementName() { - return "spot_light"; -} - -tinyxml2::XMLElement *KRSpotLight::saveXML( tinyxml2::XMLNode *parent) -{ - tinyxml2::XMLElement *e = KRLight::saveXML(parent); - e->SetAttribute("inner_angle", m_innerAngle); - e->SetAttribute("outer_angle", m_outerAngle); - return e; -} - -void KRSpotLight::loadXML(tinyxml2::XMLElement *e) { - KRLight::loadXML(e); - - e->QueryFloatAttribute("inner_angle", &m_innerAngle); - e->QueryFloatAttribute("outer_angle", &m_outerAngle); -} - -float KRSpotLight::getInnerAngle() { - return m_innerAngle; -} -float KRSpotLight::getOuterAngle() { - return m_outerAngle; -} -void KRSpotLight::setInnerAngle(float innerAngle) { - m_innerAngle = innerAngle; -} -void KRSpotLight::setOuterAngle(float outerAngle) { - m_outerAngle = outerAngle; -} - -AABB KRSpotLight::getBounds() { - float influence_radius = m_decayStart - sqrt(m_intensity * 0.01f) / sqrt(KRLIGHT_MIN_INFLUENCE); - if(influence_radius < m_flareOcclusionSize) { - influence_radius = m_flareOcclusionSize; - } - return AABB(Vector3(-influence_radius), Vector3(influence_radius), getModelMatrix()); -} - - +// +// KRSpotLight.cpp +// KREngine +// +// Created by Kearwood Gilbert on 12-04-05. +// Copyright (c) 2012 Kearwood Software. All rights reserved. +// + +#include "KREngine-common.h" +#include "KRSpotLight.h" + +KRSpotLight::KRSpotLight(KRScene &scene, std::string name) : KRLight(scene, name) +{ +} + +KRSpotLight::~KRSpotLight() +{ + +} + +std::string KRSpotLight::getElementName() { + return "spot_light"; +} + +tinyxml2::XMLElement *KRSpotLight::saveXML( tinyxml2::XMLNode *parent) +{ + tinyxml2::XMLElement *e = KRLight::saveXML(parent); + e->SetAttribute("inner_angle", m_innerAngle); + e->SetAttribute("outer_angle", m_outerAngle); + return e; +} + +void KRSpotLight::loadXML(tinyxml2::XMLElement *e) { + KRLight::loadXML(e); + + e->QueryFloatAttribute("inner_angle", &m_innerAngle); + e->QueryFloatAttribute("outer_angle", &m_outerAngle); +} + +float KRSpotLight::getInnerAngle() { + return m_innerAngle; +} +float KRSpotLight::getOuterAngle() { + return m_outerAngle; +} +void KRSpotLight::setInnerAngle(float innerAngle) { + m_innerAngle = innerAngle; +} +void KRSpotLight::setOuterAngle(float outerAngle) { + m_outerAngle = outerAngle; +} + +AABB KRSpotLight::getBounds() { + float influence_radius = m_decayStart - sqrt(m_intensity * 0.01f) / sqrt(KRLIGHT_MIN_INFLUENCE); + if(influence_radius < m_flareOcclusionSize) { + influence_radius = m_flareOcclusionSize; + } + return AABB::Create(Vector3::Create(-influence_radius), Vector3::Create(influence_radius), getModelMatrix()); +} + + diff --git a/kraken/KRSprite.cpp b/kraken/KRSprite.cpp index 7c329cd..9bc8d66 100755 --- a/kraken/KRSprite.cpp +++ b/kraken/KRSprite.cpp @@ -1,141 +1,141 @@ -// -// KRSprite.cpp -// KREngine -// -// Created by Kearwood Gilbert on 12-04-05. -// Copyright (c) 2012 Kearwood Software. All rights reserved. -// - - -#include "KREngine-common.h" -#include "KRSprite.h" - -#include "KRNode.h" -#include "KRCamera.h" -#include "KRContext.h" - -#include "KRShaderManager.h" -#include "KRShader.h" -#include "KRStockGeometry.h" -#include "KRDirectionalLight.h" -#include "KRSpotLight.h" -#include "KRPointLight.h" - - -KRSprite::KRSprite(KRScene &scene, std::string name) : KRNode(scene, name) -{ - m_spriteTexture = ""; - m_pSpriteTexture = NULL; - m_spriteAlpha = 1.0f; -} - -KRSprite::~KRSprite() -{ -} - -std::string KRSprite::getElementName() { - return "sprite"; -} - -tinyxml2::XMLElement *KRSprite::saveXML( tinyxml2::XMLNode *parent) -{ - tinyxml2::XMLElement *e = KRNode::saveXML(parent); - e->SetAttribute("sprite_texture", m_spriteTexture.c_str()); - e->SetAttribute("sprite_alpha", m_spriteAlpha); - return e; -} - -void KRSprite::loadXML(tinyxml2::XMLElement *e) { - KRNode::loadXML(e); - - if(e->QueryFloatAttribute("sprite_alpha", &m_spriteAlpha) != tinyxml2::XML_SUCCESS) { - m_spriteAlpha = 1.0f; - } - - const char *szSpriteTexture = e->Attribute("sprite_texture"); - if(szSpriteTexture) { - m_spriteTexture = szSpriteTexture; - } else { - m_spriteTexture = ""; - } - m_pSpriteTexture = NULL; -} - -void KRSprite::setSpriteTexture(std::string sprite_texture) { - m_spriteTexture = sprite_texture; - m_pSpriteTexture = NULL; -} - -void KRSprite::setSpriteAlpha(float alpha) -{ - m_spriteAlpha = alpha; -} - -float KRSprite::getSpriteAlpha() const -{ - return m_spriteAlpha; -} - -AABB KRSprite::getBounds() { - return AABB(-Vector3::One() * 0.5f, Vector3::One() * 0.5f, getModelMatrix()); -} - - -void KRSprite::render(KRCamera *pCamera, std::vector &point_lights, std::vector &directional_lights, std::vector&spot_lights, const KRViewport &viewport, KRNode::RenderPass renderPass) { - - if(m_lod_visible >= LOD_VISIBILITY_PRESTREAM && renderPass == KRNode::RENDER_PASS_PRESTREAM) { - // Pre-stream sprites, even if the alpha is zero - if(m_spriteTexture.size() && m_pSpriteTexture == NULL) { - if(!m_pSpriteTexture && m_spriteTexture.size()) { - m_pSpriteTexture = getContext().getTextureManager()->getTexture(m_spriteTexture); - } - } - - if(m_pSpriteTexture) { - m_pSpriteTexture->resetPoolExpiry(0.0f, KRTexture::TEXTURE_USAGE_SPRITE); - } - } - - if(m_lod_visible <= LOD_VISIBILITY_PRESTREAM) return; - - KRNode::render(pCamera, point_lights, directional_lights, spot_lights, viewport, renderPass); - - - if(renderPass == KRNode::RENDER_PASS_ADDITIVE_PARTICLES) { - if(m_spriteTexture.size() && m_spriteAlpha > 0.0f) { - - - if(!m_pSpriteTexture && m_spriteTexture.size()) { - m_pSpriteTexture = getContext().getTextureManager()->getTexture(m_spriteTexture); - } - - if(m_pSpriteTexture) { - /* - // Enable additive blending - GLDEBUG(glEnable(GL_BLEND)); - GLDEBUG(glBlendFunc(GL_ONE, GL_ONE)); - - // Disable z-buffer write - GLDEBUG(glDepthMask(GL_FALSE)); - */ - - // TODO - Sprites are currently additive only. Need to expose this and allow for multiple blending modes - - // Enable z-buffer test - GLDEBUG(glEnable(GL_DEPTH_TEST)); - GLDEBUG(glDepthFunc(GL_LEQUAL)); - GLDEBUG(glDepthRangef(0.0, 1.0)); - - // Render light sprite on transparency pass - KRShader *pShader = getContext().getShaderManager()->getShader("sprite", pCamera, point_lights, directional_lights, spot_lights, 0, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, renderPass); - if(getContext().getShaderManager()->selectShader(*pCamera, pShader, viewport, getModelMatrix(), point_lights, directional_lights, spot_lights, 0, renderPass, Vector3::Zero(), 0.0f, Vector4::Zero())) { - pShader->setUniform(KRShader::KRENGINE_UNIFORM_MATERIAL_ALPHA, m_spriteAlpha); - m_pContext->getTextureManager()->selectTexture(0, m_pSpriteTexture, 0.0f, KRTexture::TEXTURE_USAGE_SPRITE); - m_pContext->getMeshManager()->bindVBO(&m_pContext->getMeshManager()->KRENGINE_VBO_DATA_2D_SQUARE_VERTICES, 1.0f); - GLDEBUG(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)); - } - } - } - - } -} +// +// KRSprite.cpp +// KREngine +// +// Created by Kearwood Gilbert on 12-04-05. +// Copyright (c) 2012 Kearwood Software. All rights reserved. +// + + +#include "KREngine-common.h" +#include "KRSprite.h" + +#include "KRNode.h" +#include "KRCamera.h" +#include "KRContext.h" + +#include "KRShaderManager.h" +#include "KRShader.h" +#include "KRStockGeometry.h" +#include "KRDirectionalLight.h" +#include "KRSpotLight.h" +#include "KRPointLight.h" + + +KRSprite::KRSprite(KRScene &scene, std::string name) : KRNode(scene, name) +{ + m_spriteTexture = ""; + m_pSpriteTexture = NULL; + m_spriteAlpha = 1.0f; +} + +KRSprite::~KRSprite() +{ +} + +std::string KRSprite::getElementName() { + return "sprite"; +} + +tinyxml2::XMLElement *KRSprite::saveXML( tinyxml2::XMLNode *parent) +{ + tinyxml2::XMLElement *e = KRNode::saveXML(parent); + e->SetAttribute("sprite_texture", m_spriteTexture.c_str()); + e->SetAttribute("sprite_alpha", m_spriteAlpha); + return e; +} + +void KRSprite::loadXML(tinyxml2::XMLElement *e) { + KRNode::loadXML(e); + + if(e->QueryFloatAttribute("sprite_alpha", &m_spriteAlpha) != tinyxml2::XML_SUCCESS) { + m_spriteAlpha = 1.0f; + } + + const char *szSpriteTexture = e->Attribute("sprite_texture"); + if(szSpriteTexture) { + m_spriteTexture = szSpriteTexture; + } else { + m_spriteTexture = ""; + } + m_pSpriteTexture = NULL; +} + +void KRSprite::setSpriteTexture(std::string sprite_texture) { + m_spriteTexture = sprite_texture; + m_pSpriteTexture = NULL; +} + +void KRSprite::setSpriteAlpha(float alpha) +{ + m_spriteAlpha = alpha; +} + +float KRSprite::getSpriteAlpha() const +{ + return m_spriteAlpha; +} + +AABB KRSprite::getBounds() { + return AABB::Create(-Vector3::One() * 0.5f, Vector3::One() * 0.5f, getModelMatrix()); +} + + +void KRSprite::render(KRCamera *pCamera, std::vector &point_lights, std::vector &directional_lights, std::vector&spot_lights, const KRViewport &viewport, KRNode::RenderPass renderPass) { + + if(m_lod_visible >= LOD_VISIBILITY_PRESTREAM && renderPass == KRNode::RENDER_PASS_PRESTREAM) { + // Pre-stream sprites, even if the alpha is zero + if(m_spriteTexture.size() && m_pSpriteTexture == NULL) { + if(!m_pSpriteTexture && m_spriteTexture.size()) { + m_pSpriteTexture = getContext().getTextureManager()->getTexture(m_spriteTexture); + } + } + + if(m_pSpriteTexture) { + m_pSpriteTexture->resetPoolExpiry(0.0f, KRTexture::TEXTURE_USAGE_SPRITE); + } + } + + if(m_lod_visible <= LOD_VISIBILITY_PRESTREAM) return; + + KRNode::render(pCamera, point_lights, directional_lights, spot_lights, viewport, renderPass); + + + if(renderPass == KRNode::RENDER_PASS_ADDITIVE_PARTICLES) { + if(m_spriteTexture.size() && m_spriteAlpha > 0.0f) { + + + if(!m_pSpriteTexture && m_spriteTexture.size()) { + m_pSpriteTexture = getContext().getTextureManager()->getTexture(m_spriteTexture); + } + + if(m_pSpriteTexture) { + /* + // Enable additive blending + GLDEBUG(glEnable(GL_BLEND)); + GLDEBUG(glBlendFunc(GL_ONE, GL_ONE)); + + // Disable z-buffer write + GLDEBUG(glDepthMask(GL_FALSE)); + */ + + // TODO - Sprites are currently additive only. Need to expose this and allow for multiple blending modes + + // Enable z-buffer test + GLDEBUG(glEnable(GL_DEPTH_TEST)); + GLDEBUG(glDepthFunc(GL_LEQUAL)); + GLDEBUG(glDepthRangef(0.0, 1.0)); + + // Render light sprite on transparency pass + KRShader *pShader = getContext().getShaderManager()->getShader("sprite", pCamera, point_lights, directional_lights, spot_lights, 0, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, renderPass); + if(getContext().getShaderManager()->selectShader(*pCamera, pShader, viewport, getModelMatrix(), point_lights, directional_lights, spot_lights, 0, renderPass, Vector3::Zero(), 0.0f, Vector4::Zero())) { + pShader->setUniform(KRShader::KRENGINE_UNIFORM_MATERIAL_ALPHA, m_spriteAlpha); + m_pContext->getTextureManager()->selectTexture(0, m_pSpriteTexture, 0.0f, KRTexture::TEXTURE_USAGE_SPRITE); + m_pContext->getMeshManager()->bindVBO(&m_pContext->getMeshManager()->KRENGINE_VBO_DATA_2D_SQUARE_VERTICES, 1.0f); + GLDEBUG(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)); + } + } + } + + } +} diff --git a/kraken/KRTextureTGA.cpp b/kraken/KRTextureTGA.cpp index 892d157..a821f3d 100755 --- a/kraken/KRTextureTGA.cpp +++ b/kraken/KRTextureTGA.cpp @@ -1,385 +1,385 @@ -// -// KRTextureTGA.cpp -// KREngine -// -// Created by Kearwood Gilbert on 2012-10-23. -// Copyright (c) 2012 Kearwood Software. All rights reserved. -// - -#include "KRTextureTGA.h" -#include "KREngine-common.h" -#include "KRContext.h" -#include "KRTextureKTX.h" - -#if defined(_WIN32) || defined(_WIN64) -#pragma pack(1) -typedef struct { - char idlength; - char colourmaptype; - char imagetype; - short int colourmaporigin; - short int colourmaplength; - char colourmapdepth; - short int x_origin; - short int y_origin; - short width; - short height; - char bitsperpixel; - char imagedescriptor; -} TGA_HEADER; -#pragma pack() -#else -typedef struct { - char idlength; - char colourmaptype; - char imagetype; - short int colourmaporigin; - short int colourmaplength; - char colourmapdepth; - short int x_origin; - short int y_origin; - short width; - short height; - char bitsperpixel; - char imagedescriptor; -} __attribute__((packed)) TGA_HEADER; -#endif - - -KRTextureTGA::KRTextureTGA(KRContext &context, KRDataBlock *data, std::string name) : KRTexture2D(context, data, name) -{ - data->lock(); - TGA_HEADER *pHeader = (TGA_HEADER *)data->getStart(); - - m_max_lod_max_dim = pHeader->width > pHeader->height ? pHeader->width : pHeader->height; - m_min_lod_max_dim = m_max_lod_max_dim; // Mipmaps not yet supported for TGA images - switch(pHeader->imagetype) { - case 2: // rgb - case 10: // rgb + rle - switch(pHeader->bitsperpixel) { - case 24: - { - m_imageSize = pHeader->width * pHeader->height * 4; - } - break; - case 32: - { - m_imageSize = pHeader->width * pHeader->height * 4; - } - break; - - default: - { - assert(false); - } - break; - } - break; - default: - { - assert(false); - break; - } - } - - data->unlock(); -} - -KRTextureTGA::~KRTextureTGA() -{ - -} - -bool KRTextureTGA::uploadTexture(GLenum target, int lod_max_dim, int ¤t_lod_max_dim, bool compress, bool premultiply_alpha) -{ - m_pData->lock(); - TGA_HEADER *pHeader = (TGA_HEADER *)m_pData->getStart(); - unsigned char *pData = (unsigned char *)pHeader + (long)pHeader->idlength + (long)pHeader->colourmaplength * (long)pHeader->colourmaptype + sizeof(TGA_HEADER); - - GLenum internal_format = GL_RGBA; - -#if !TARGET_OS_IPHONE - if(compress) { - internal_format = pHeader->bitsperpixel == 24 ? GL_COMPRESSED_RGB_S3TC_DXT1_EXT : GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; - } -#endif - - if(pHeader->colourmaptype != 0) { - m_pData->unlock(); - return false; // Mapped colors not supported - } - - switch(pHeader->imagetype) { - case 2: // rgb - switch(pHeader->bitsperpixel) { - case 24: - { - unsigned char *converted_image = (unsigned char *)malloc(pHeader->width * pHeader->height * 4); -//#ifdef __APPLE__ -// vImage_Buffer source_image = { pData, pHeader->height, pHeader->width, pHeader->width*3 }; -// vImage_Buffer dest_image = { converted_image, pHeader->height, pHeader->width, pHeader->width*4 }; -// vImageConvert_RGB888toRGBA8888(&source_image, NULL, 0xff, &dest_image, false, kvImageDoNotTile); -//#else - unsigned char *pSource = pData; - unsigned char *pDest = converted_image; - unsigned char *pEnd = pData + pHeader->height * pHeader->width * 3; - while(pSource < pEnd) { - *pDest++ = pSource[0]; - *pDest++ = pSource[1]; - *pDest++ = pSource[2]; - *pDest++ = 0xff; - pSource += 3; - } - assert(pSource <= m_pData->getEnd()); -//#endif - GLDEBUG(glTexImage2D(target, 0, internal_format, pHeader->width, pHeader->height, 0, GL_BGRA, GL_UNSIGNED_BYTE, (GLvoid *)converted_image)); - GLDEBUG(glFinish()); - free(converted_image); - - current_lod_max_dim = m_max_lod_max_dim; - } - break; - case 32: - { - if(premultiply_alpha) { - unsigned char *converted_image = (unsigned char *)malloc(pHeader->width * pHeader->height * 4); - - unsigned char *pSource = pData; - unsigned char *pDest = converted_image; - unsigned char *pEnd = pData + pHeader->height * pHeader->width * 3; - while(pSource < pEnd) { - *pDest++ = (__uint32_t)pSource[0] * (__uint32_t)pSource[3] / 0xff; - *pDest++ = (__uint32_t)pSource[1] * (__uint32_t)pSource[3] / 0xff; - *pDest++ = (__uint32_t)pSource[2] * (__uint32_t)pSource[3] / 0xff; - *pDest++ = pSource[3]; - pSource += 4; - } - assert(pSource <= m_pData->getEnd()); - - GLDEBUG(glTexImage2D(target, 0, internal_format, pHeader->width, pHeader->height, 0, GL_BGRA, GL_UNSIGNED_BYTE, (GLvoid *)converted_image)); - GLDEBUG(glFinish()); - free(converted_image); - } else { - GLDEBUG(glTexImage2D(target, 0, internal_format, pHeader->width, pHeader->height, 0, GL_BGRA, GL_UNSIGNED_BYTE, (GLvoid *)pData)); - GLDEBUG(glFinish()); - } - - current_lod_max_dim = m_max_lod_max_dim; - } - break; - default: - m_pData->unlock(); - return false; // 16-bit images not yet supported - } - break; - case 10: // rgb + rle - switch(pHeader->bitsperpixel) { - case 32: - { - unsigned char *converted_image = (unsigned char *)malloc(pHeader->width * pHeader->height * 4); - unsigned char *pSource = pData; - unsigned char *pDest = converted_image; - unsigned char *pEnd = converted_image + pHeader->height * pHeader->width * 4; - if(premultiply_alpha) { - while(pDest < pEnd) { - int count = (*pSource & 0x7f) + 1; - if(*pSource & 0x80) { - // RLE Packet - pSource++; - while(count--) { - *pDest++ = (__uint32_t)pSource[0] * (__uint32_t)pSource[3] / 0xff; - *pDest++ = (__uint32_t)pSource[1] * (__uint32_t)pSource[3] / 0xff; - *pDest++ = (__uint32_t)pSource[2] * (__uint32_t)pSource[3] / 0xff; - *pDest++ = pSource[3]; - } - pSource += 4; - } else { - // RAW Packet - pSource++; - while(count--) { - *pDest++ = (__uint32_t)pSource[0] * (__uint32_t)pSource[3] / 0xff; - *pDest++ = (__uint32_t)pSource[1] * (__uint32_t)pSource[3] / 0xff; - *pDest++ = (__uint32_t)pSource[2] * (__uint32_t)pSource[3] / 0xff; - *pDest++ = pSource[3]; - pSource += 4; - } - } - } - assert(pSource <= m_pData->getEnd()); - assert(pDest == pEnd); - } else { - while(pDest < pEnd) { - int count = (*pSource & 0x7f) + 1; - if(*pSource & 0x80) { - // RLE Packet - pSource++; - while(count--) { - *pDest++ = pSource[0]; - *pDest++ = pSource[1]; - *pDest++ = pSource[2]; - *pDest++ = pSource[3]; - } - pSource += 4; - } else { - // RAW Packet - pSource++; - while(count--) { - *pDest++ = pSource[0]; - *pDest++ = pSource[1]; - *pDest++ = pSource[2]; - *pDest++ = pSource[3]; - pSource += 4; - } - } - } - assert(pSource <= m_pData->getEnd()); - assert(pDest == pEnd); - } - GLDEBUG(glTexImage2D(target, 0, internal_format, pHeader->width, pHeader->height, 0, GL_BGRA, GL_UNSIGNED_BYTE, (GLvoid *)converted_image)); - GLDEBUG(glFinish()); - free(converted_image); - current_lod_max_dim = m_max_lod_max_dim; - } - break; - case 24: - { - unsigned char *converted_image = (unsigned char *)malloc(pHeader->width * pHeader->height * 4); - unsigned char *pSource = pData; - unsigned char *pDest = converted_image; - unsigned char *pEnd = converted_image + pHeader->height * pHeader->width * 4; - while(pDest < pEnd) { - int count = (*pSource & 0x7f) + 1; - if(*pSource & 0x80) { - // RLE Packet - pSource++; - while(count--) { - *pDest++ = pSource[0]; - *pDest++ = pSource[1]; - *pDest++ = pSource[2]; - *pDest++ = 0xff; - } - pSource += 3; - } else { - // RAW Packet - pSource++; - while(count--) { - *pDest++ = pSource[0]; - *pDest++ = pSource[1]; - *pDest++ = pSource[2]; - *pDest++ = 0xff; - pSource += 3; - } - } - } - assert(pSource <= m_pData->getEnd()); - assert(pDest == pEnd); - GLDEBUG(glTexImage2D(target, 0, internal_format, pHeader->width, pHeader->height, 0, GL_BGRA, GL_UNSIGNED_BYTE, (GLvoid *)converted_image)); - GLDEBUG(glFinish()); - free(converted_image); - current_lod_max_dim = m_max_lod_max_dim; - } - break; - default: - m_pData->unlock(); - return false; // 16-bit images not yet supported - } - break; - default: - m_pData->unlock(); - return false; // Image type not yet supported - } - - m_pData->unlock(); - return true; -} - -#if !TARGET_OS_IPHONE - -KRTexture *KRTextureTGA::compress(bool premultiply_alpha) -{ - m_pData->lock(); - - std::list blocks; - - getContext().getTextureManager()->_setActiveTexture(0); - - GLuint compressed_handle = 0; - GLDEBUG(glGenTextures(1, &compressed_handle)); - - GLDEBUG(glBindTexture(GL_TEXTURE_2D, compressed_handle)); - - int current_max_dim = 0; - if(!uploadTexture(GL_TEXTURE_2D, m_max_lod_max_dim, current_max_dim, true, premultiply_alpha)) { - assert(false); // Failed to upload the texture - } - GLDEBUG(glGenerateMipmap(GL_TEXTURE_2D)); - - GLint width = 0, height = 0, internal_format, base_internal_format; - - - GLDEBUG(glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &width)); - GLDEBUG(glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &height)); - GLDEBUG(glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT, &internal_format)); - - /* - int texture_base_level = 0; - int texture_max_level = 0; - GLDEBUG(glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MIN_LOD, &texture_base_level)); - GLDEBUG(glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MAX_LOD, &texture_max_level)); - */ - switch(internal_format) - { - case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: - base_internal_format = GL_BGRA; - break; - case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: - base_internal_format = GL_BGRA; - break; - default: - assert(false); // Not yet supported - break; - } - - GLuint lod_level = 0; - GLint compressed_size = 0; - int lod_width = width; - while(lod_width > 1) { - GLDEBUG(glGetTexLevelParameteriv(GL_TEXTURE_2D, lod_level, GL_TEXTURE_WIDTH, &lod_width)); - GLDEBUG(glGetTexLevelParameteriv(GL_TEXTURE_2D, lod_level, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &compressed_size)); - KRDataBlock *new_block = new KRDataBlock(); - new_block->expand(compressed_size); - new_block->lock(); - GLDEBUG(glGetCompressedTexImage(GL_TEXTURE_2D, lod_level, new_block->getStart())); - new_block->unlock(); - blocks.push_back(new_block); - - lod_level++; - } - assert(lod_width == 1); - - GLDEBUG(glBindTexture(GL_TEXTURE_2D, 0)); - getContext().getTextureManager()->selectTexture(0, NULL, 0.0f, KRTexture::TEXTURE_USAGE_NONE); - GLDEBUG(glDeleteTextures(1, &compressed_handle)); - - KRTextureKTX *new_texture = new KRTextureKTX(getContext(), getName(), internal_format, base_internal_format, width, height, blocks); - - m_pData->unlock(); - - for(auto block_itr = blocks.begin(); block_itr != blocks.end(); block_itr++) { - KRDataBlock *block = *block_itr; - delete block; - } - - return new_texture; -} -#endif - -long KRTextureTGA::getMemRequiredForSize(int max_dim) -{ - return m_imageSize; -} - -std::string KRTextureTGA::getExtension() -{ - return "tga"; -} +// +// KRTextureTGA.cpp +// KREngine +// +// Created by Kearwood Gilbert on 2012-10-23. +// Copyright (c) 2012 Kearwood Software. All rights reserved. +// + +#include "KRTextureTGA.h" +#include "KREngine-common.h" +#include "KRContext.h" +#include "KRTextureKTX.h" + +#if defined(_WIN32) || defined(_WIN64) +#pragma pack(1) +typedef struct { + char idlength; + char colourmaptype; + char imagetype; + short int colourmaporigin; + short int colourmaplength; + char colourmapdepth; + short int x_origin; + short int y_origin; + short width; + short height; + char bitsperpixel; + char imagedescriptor; +} TGA_HEADER; +#pragma pack() +#else +typedef struct { + char idlength; + char colourmaptype; + char imagetype; + short int colourmaporigin; + short int colourmaplength; + char colourmapdepth; + short int x_origin; + short int y_origin; + short width; + short height; + char bitsperpixel; + char imagedescriptor; +} __attribute__((packed)) TGA_HEADER; +#endif + + +KRTextureTGA::KRTextureTGA(KRContext &context, KRDataBlock *data, std::string name) : KRTexture2D(context, data, name) +{ + data->lock(); + TGA_HEADER *pHeader = (TGA_HEADER *)data->getStart(); + + m_max_lod_max_dim = pHeader->width > pHeader->height ? pHeader->width : pHeader->height; + m_min_lod_max_dim = m_max_lod_max_dim; // Mipmaps not yet supported for TGA images + switch(pHeader->imagetype) { + case 2: // rgb + case 10: // rgb + rle + switch(pHeader->bitsperpixel) { + case 24: + { + m_imageSize = pHeader->width * pHeader->height * 4; + } + break; + case 32: + { + m_imageSize = pHeader->width * pHeader->height * 4; + } + break; + + default: + { + assert(false); + } + break; + } + break; + default: + { + assert(false); + break; + } + } + + data->unlock(); +} + +KRTextureTGA::~KRTextureTGA() +{ + +} + +bool KRTextureTGA::uploadTexture(GLenum target, int lod_max_dim, int ¤t_lod_max_dim, bool compress, bool premultiply_alpha) +{ + m_pData->lock(); + TGA_HEADER *pHeader = (TGA_HEADER *)m_pData->getStart(); + unsigned char *pData = (unsigned char *)pHeader + (long)pHeader->idlength + (long)pHeader->colourmaplength * (long)pHeader->colourmaptype + sizeof(TGA_HEADER); + + GLenum internal_format = GL_RGBA; + +#if !TARGET_OS_IPHONE + if(compress) { + internal_format = pHeader->bitsperpixel == 24 ? GL_COMPRESSED_RGB_S3TC_DXT1_EXT : GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; + } +#endif + + if(pHeader->colourmaptype != 0) { + m_pData->unlock(); + return false; // Mapped colors not supported + } + + switch(pHeader->imagetype) { + case 2: // rgb + switch(pHeader->bitsperpixel) { + case 24: + { + unsigned char *converted_image = (unsigned char *)malloc(pHeader->width * pHeader->height * 4); +//#ifdef __APPLE__ +// vImage_Buffer source_image = { pData, pHeader->height, pHeader->width, pHeader->width*3 }; +// vImage_Buffer dest_image = { converted_image, pHeader->height, pHeader->width, pHeader->width*4 }; +// vImageConvert_RGB888toRGBA8888(&source_image, NULL, 0xff, &dest_image, false, kvImageDoNotTile); +//#else + unsigned char *pSource = pData; + unsigned char *pDest = converted_image; + unsigned char *pEnd = pData + pHeader->height * pHeader->width * 3; + while(pSource < pEnd) { + *pDest++ = pSource[0]; + *pDest++ = pSource[1]; + *pDest++ = pSource[2]; + *pDest++ = 0xff; + pSource += 3; + } + assert(pSource <= m_pData->getEnd()); +//#endif + GLDEBUG(glTexImage2D(target, 0, internal_format, pHeader->width, pHeader->height, 0, GL_BGRA, GL_UNSIGNED_BYTE, (GLvoid *)converted_image)); + GLDEBUG(glFinish()); + free(converted_image); + + current_lod_max_dim = m_max_lod_max_dim; + } + break; + case 32: + { + if(premultiply_alpha) { + unsigned char *converted_image = (unsigned char *)malloc(pHeader->width * pHeader->height * 4); + + unsigned char *pSource = pData; + unsigned char *pDest = converted_image; + unsigned char *pEnd = pData + pHeader->height * pHeader->width * 3; + while(pSource < pEnd) { + *pDest++ = (__uint32_t)pSource[0] * (__uint32_t)pSource[3] / 0xff; + *pDest++ = (__uint32_t)pSource[1] * (__uint32_t)pSource[3] / 0xff; + *pDest++ = (__uint32_t)pSource[2] * (__uint32_t)pSource[3] / 0xff; + *pDest++ = pSource[3]; + pSource += 4; + } + assert(pSource <= m_pData->getEnd()); + + GLDEBUG(glTexImage2D(target, 0, internal_format, pHeader->width, pHeader->height, 0, GL_BGRA, GL_UNSIGNED_BYTE, (GLvoid *)converted_image)); + GLDEBUG(glFinish()); + free(converted_image); + } else { + GLDEBUG(glTexImage2D(target, 0, internal_format, pHeader->width, pHeader->height, 0, GL_BGRA, GL_UNSIGNED_BYTE, (GLvoid *)pData)); + GLDEBUG(glFinish()); + } + + current_lod_max_dim = m_max_lod_max_dim; + } + break; + default: + m_pData->unlock(); + return false; // 16-bit images not yet supported + } + break; + case 10: // rgb + rle + switch(pHeader->bitsperpixel) { + case 32: + { + unsigned char *converted_image = (unsigned char *)malloc(pHeader->width * pHeader->height * 4); + unsigned char *pSource = pData; + unsigned char *pDest = converted_image; + unsigned char *pEnd = converted_image + pHeader->height * pHeader->width * 4; + if(premultiply_alpha) { + while(pDest < pEnd) { + int count = (*pSource & 0x7f) + 1; + if(*pSource & 0x80) { + // RLE Packet + pSource++; + while(count--) { + *pDest++ = (__uint32_t)pSource[0] * (__uint32_t)pSource[3] / 0xff; + *pDest++ = (__uint32_t)pSource[1] * (__uint32_t)pSource[3] / 0xff; + *pDest++ = (__uint32_t)pSource[2] * (__uint32_t)pSource[3] / 0xff; + *pDest++ = pSource[3]; + } + pSource += 4; + } else { + // RAW Packet + pSource++; + while(count--) { + *pDest++ = (__uint32_t)pSource[0] * (__uint32_t)pSource[3] / 0xff; + *pDest++ = (__uint32_t)pSource[1] * (__uint32_t)pSource[3] / 0xff; + *pDest++ = (__uint32_t)pSource[2] * (__uint32_t)pSource[3] / 0xff; + *pDest++ = pSource[3]; + pSource += 4; + } + } + } + assert(pSource <= m_pData->getEnd()); + assert(pDest == pEnd); + } else { + while(pDest < pEnd) { + int count = (*pSource & 0x7f) + 1; + if(*pSource & 0x80) { + // RLE Packet + pSource++; + while(count--) { + *pDest++ = pSource[0]; + *pDest++ = pSource[1]; + *pDest++ = pSource[2]; + *pDest++ = pSource[3]; + } + pSource += 4; + } else { + // RAW Packet + pSource++; + while(count--) { + *pDest++ = pSource[0]; + *pDest++ = pSource[1]; + *pDest++ = pSource[2]; + *pDest++ = pSource[3]; + pSource += 4; + } + } + } + assert(pSource <= m_pData->getEnd()); + assert(pDest == pEnd); + } + GLDEBUG(glTexImage2D(target, 0, internal_format, pHeader->width, pHeader->height, 0, GL_BGRA, GL_UNSIGNED_BYTE, (GLvoid *)converted_image)); + GLDEBUG(glFinish()); + free(converted_image); + current_lod_max_dim = m_max_lod_max_dim; + } + break; + case 24: + { + unsigned char *converted_image = (unsigned char *)malloc(pHeader->width * pHeader->height * 4); + unsigned char *pSource = pData; + unsigned char *pDest = converted_image; + unsigned char *pEnd = converted_image + pHeader->height * pHeader->width * 4; + while(pDest < pEnd) { + int count = (*pSource & 0x7f) + 1; + if(*pSource & 0x80) { + // RLE Packet + pSource++; + while(count--) { + *pDest++ = pSource[0]; + *pDest++ = pSource[1]; + *pDest++ = pSource[2]; + *pDest++ = 0xff; + } + pSource += 3; + } else { + // RAW Packet + pSource++; + while(count--) { + *pDest++ = pSource[0]; + *pDest++ = pSource[1]; + *pDest++ = pSource[2]; + *pDest++ = 0xff; + pSource += 3; + } + } + } + assert(pSource <= m_pData->getEnd()); + assert(pDest == pEnd); + GLDEBUG(glTexImage2D(target, 0, internal_format, pHeader->width, pHeader->height, 0, GL_BGRA, GL_UNSIGNED_BYTE, (GLvoid *)converted_image)); + GLDEBUG(glFinish()); + free(converted_image); + current_lod_max_dim = m_max_lod_max_dim; + } + break; + default: + m_pData->unlock(); + return false; // 16-bit images not yet supported + } + break; + default: + m_pData->unlock(); + return false; // Image type not yet supported + } + + m_pData->unlock(); + return true; +} + +#if !TARGET_OS_IPHONE + +KRTexture *KRTextureTGA::compress(bool premultiply_alpha) +{ + m_pData->lock(); + + std::list blocks; + + getContext().getTextureManager()->_setActiveTexture(0); + + GLuint compressed_handle = 0; + GLDEBUG(glGenTextures(1, &compressed_handle)); + + GLDEBUG(glBindTexture(GL_TEXTURE_2D, compressed_handle)); + + int current_max_dim = 0; + if(!uploadTexture(GL_TEXTURE_2D, m_max_lod_max_dim, current_max_dim, true, premultiply_alpha)) { + assert(false); // Failed to upload the texture + } + GLDEBUG(glGenerateMipmap(GL_TEXTURE_2D)); + + GLint width = 0, height = 0, internal_format, base_internal_format; + + + GLDEBUG(glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &width)); + GLDEBUG(glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &height)); + GLDEBUG(glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT, &internal_format)); + + /* + int texture_base_level = 0; + int texture_max_level = 0; + GLDEBUG(glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MIN_LOD, &texture_base_level)); + GLDEBUG(glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MAX_LOD, &texture_max_level)); + */ + switch(internal_format) + { + case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: + base_internal_format = GL_BGRA; + break; + case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: + base_internal_format = GL_BGRA; + break; + default: + assert(false); // Not yet supported + break; + } + + GLuint lod_level = 0; + GLint compressed_size = 0; + int lod_width = width; + while(lod_width > 1) { + GLDEBUG(glGetTexLevelParameteriv(GL_TEXTURE_2D, lod_level, GL_TEXTURE_WIDTH, &lod_width)); + GLDEBUG(glGetTexLevelParameteriv(GL_TEXTURE_2D, lod_level, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &compressed_size)); + KRDataBlock *new_block = new KRDataBlock(); + new_block->expand(compressed_size); + new_block->lock(); + GLDEBUG(glGetCompressedTexImage(GL_TEXTURE_2D, lod_level, new_block->getStart())); + new_block->unlock(); + blocks.push_back(new_block); + + lod_level++; + } + assert(lod_width == 1); + + GLDEBUG(glBindTexture(GL_TEXTURE_2D, 0)); + getContext().getTextureManager()->selectTexture(0, NULL, 0.0f, KRTexture::TEXTURE_USAGE_NONE); + GLDEBUG(glDeleteTextures(1, &compressed_handle)); + + KRTextureKTX *new_texture = new KRTextureKTX(getContext(), getName(), internal_format, base_internal_format, width, height, blocks); + + m_pData->unlock(); + + for(auto block_itr = blocks.begin(); block_itr != blocks.end(); block_itr++) { + KRDataBlock *block = *block_itr; + delete block; + } + + return new_texture; +} +#endif + +long KRTextureTGA::getMemRequiredForSize(int max_dim) +{ + return m_imageSize; +} + +std::string KRTextureTGA::getExtension() +{ + return "tga"; +} diff --git a/kraken/KRViewport.cpp b/kraken/KRViewport.cpp index 61f9f99..2153301 100755 --- a/kraken/KRViewport.cpp +++ b/kraken/KRViewport.cpp @@ -1,264 +1,264 @@ -// -// KRViewport.cpp -// KREngine -// -// Created by Kearwood Gilbert on 2012-10-25. -// Copyright (c) 2012 Kearwood Software. All rights reserved. -// - -#define KRENGINE_SWAP_INT(x,y) {int t;t=x;x=y;y=t;} - -#include "KREngine-common.h" - -#include "KRViewport.h" - -KRViewport::KRViewport() -{ - m_size = Vector2::One(); - m_matProjection = Matrix4(); - m_matView = Matrix4(); - m_lodBias = 0.0f; - calculateDerivedValues(); -} - -KRViewport::KRViewport(const Vector2 &size, const Matrix4 &matView, const Matrix4 &matProjection) -{ - m_size = size; - m_matView = matView; - m_matProjection = matProjection; - calculateDerivedValues(); -} - - -KRViewport& KRViewport::operator=(const KRViewport &v) { - if(this != &v) { // Prevent self-assignment. - m_size = v.m_size; - m_matProjection = v.m_matProjection; - m_matView = v.m_matView; - m_lodBias = v.m_lodBias; - - calculateDerivedValues(); - } - return *this; -} - - -KRViewport::~KRViewport() -{ - -} - -const Vector2 &KRViewport::getSize() const -{ - return m_size; -} - -const Matrix4 &KRViewport::getViewMatrix() const -{ - return m_matView; -} - -const Matrix4 &KRViewport::getProjectionMatrix() const -{ - return m_matProjection; -} - -void KRViewport::setSize(const Vector2 &size) -{ - m_size = size; -} - -void KRViewport::setViewMatrix(const Matrix4 &matView) -{ - m_matView = matView; - calculateDerivedValues(); -} - -void KRViewport::setProjectionMatrix(const Matrix4 &matProjection) -{ - m_matProjection = matProjection; - calculateDerivedValues(); -} - -const Matrix4 &KRViewport::KRViewport::getViewProjectionMatrix() const -{ - return m_matViewProjection; -} - -const Matrix4 &KRViewport::getInverseViewMatrix() const -{ - return m_matInverseView; -} - -const Matrix4 &KRViewport::getInverseProjectionMatrix() const -{ - return m_matInverseProjection; -} - -const Vector3 &KRViewport::getCameraDirection() const -{ - return m_cameraDirection; -} - -const Vector3 &KRViewport::getCameraPosition() const -{ - return m_cameraPosition; -} - -const int *KRViewport::getFrontToBackOrder() const -{ - return &m_frontToBackOrder[0]; -} - -const int *KRViewport::getBackToFrontOrder() const -{ - return &m_backToFrontOrder[0]; -} - -void KRViewport::calculateDerivedValues() -{ - m_matViewProjection = m_matView * m_matProjection; - m_matInverseView = Matrix4::Invert(m_matView); - m_matInverseProjection = Matrix4::Invert(m_matProjection); - m_cameraPosition = Matrix4::Dot(m_matInverseView, Vector3::Zero()); - m_cameraDirection = Matrix4::Dot(m_matInverseView, Vector3(0.0, 0.0, 1.0)) - Matrix4::Dot(m_matInverseView, Vector3(0.0, 0.0, 0.0)); - - for(int i=0; i<8; i++) { - m_frontToBackOrder[i] = i; - } - - if(m_cameraDirection.x > 0.0) { - KRENGINE_SWAP_INT(m_frontToBackOrder[0], m_frontToBackOrder[1]); - KRENGINE_SWAP_INT(m_frontToBackOrder[2], m_frontToBackOrder[3]); - KRENGINE_SWAP_INT(m_frontToBackOrder[4], m_frontToBackOrder[5]); - KRENGINE_SWAP_INT(m_frontToBackOrder[6], m_frontToBackOrder[7]); - } - - if(m_cameraDirection.y > 0.0) { - KRENGINE_SWAP_INT(m_frontToBackOrder[0], m_frontToBackOrder[2]); - KRENGINE_SWAP_INT(m_frontToBackOrder[1], m_frontToBackOrder[3]); - KRENGINE_SWAP_INT(m_frontToBackOrder[4], m_frontToBackOrder[6]); - KRENGINE_SWAP_INT(m_frontToBackOrder[5], m_frontToBackOrder[7]); - } - - if(m_cameraDirection.z > 0.0) { - KRENGINE_SWAP_INT(m_frontToBackOrder[0], m_frontToBackOrder[4]); - KRENGINE_SWAP_INT(m_frontToBackOrder[1], m_frontToBackOrder[5]); - KRENGINE_SWAP_INT(m_frontToBackOrder[2], m_frontToBackOrder[6]); - KRENGINE_SWAP_INT(m_frontToBackOrder[3], m_frontToBackOrder[7]); - } - - for(int i=0; i<8; i++) { - m_backToFrontOrder[i] = m_frontToBackOrder[7-i]; - } -} - - -unordered_map &KRViewport::getVisibleBounds() -{ - return m_visibleBounds; -} - -float KRViewport::getLODBias() const -{ - return m_lodBias; -} - -void KRViewport::setLODBias(float lod_bias) -{ - m_lodBias = lod_bias; -} - -float KRViewport::coverage(const AABB &b) const -{ - if(!visible(b)) { - return 0.0f; // Culled out by view frustrum - } else { - Vector3 nearest_point = b.nearestPoint(getCameraPosition()); - float distance = (nearest_point - getCameraPosition()).magnitude(); - - Vector3 v = Matrix4::DotWDiv(m_matProjection, getCameraPosition() + getCameraDirection() * distance); - - float screen_depth = distance / 1000.0f; - - return KRCLAMP(1.0f - screen_depth, 0.01f, 1.0f); - - /* - - Vector2 screen_min; - Vector2 screen_max; - // Loop through all corners and transform them to screen space - for(int i=0; i<8; i++) { - Vector3 screen_pos = Matrix4::DotWDiv(m_matViewProjection, Vector3(i & 1 ? b.min.x : b.max.x, i & 2 ? b.min.y : b.max.y, i & 4 ? b.min.z : b.max.z)); - if(i==0) { - screen_min = screen_pos.xy(); - screen_max = screen_pos.xy(); - } else { - if(screen_pos.x < screen_min.x) screen_min.x = screen_pos.x; - if(screen_pos.y < screen_min.y) screen_min.y = screen_pos.y; - if(screen_pos.x > screen_max.x) screen_max.x = screen_pos.x; - if(screen_pos.y > screen_max.y) screen_max.y = screen_pos.y; - } - } - - screen_min.x = KRCLAMP(screen_min.x, 0.0f, 1.0f); - screen_min.y = KRCLAMP(screen_min.y, 0.0f, 1.0f); - screen_max.x = KRCLAMP(screen_max.x, 0.0f, 1.0f); - screen_max.y = KRCLAMP(screen_max.y, 0.0f, 1.0f); - - float c = (screen_max.x - screen_min.x) * (screen_max.y - screen_min.y); - return KRCLAMP(c, 0.01f, 1.0f); - */ - } -} - - -bool KRViewport::visible(const AABB &b) const -{ - // test if bounding box would be within the visible range of the clip space transformed by matViewProjection - // This is used for view frustrum culling - - int outside_count[6] = {0, 0, 0, 0, 0, 0}; - - for(int iCorner=0; iCorner<8; iCorner++) { - Vector4 sourceCornerVertex = Vector4( - (iCorner & 1) == 0 ? b.min.x : b.max.x, - (iCorner & 2) == 0 ? b.min.y : b.max.y, - (iCorner & 4) == 0 ? b.min.z : b.max.z, 1.0f); - - Vector4 cornerVertex = Matrix4::Dot4(m_matViewProjection, sourceCornerVertex); - - if(cornerVertex.x < -cornerVertex.w) { - outside_count[0]++; - } - if(cornerVertex.y < -cornerVertex.w) { - outside_count[1]++; - } - if(cornerVertex.z < -cornerVertex.w) { - outside_count[2]++; - } - if(cornerVertex.x > cornerVertex.w) { - outside_count[3]++; - } - if(cornerVertex.y > cornerVertex.w) { - outside_count[4]++; - } - if(cornerVertex.z > cornerVertex.w) { - outside_count[5]++; - } - } - - bool is_visible = true; - for(int iFace=0; iFace < 6; iFace++) { - if(outside_count[iFace] == 8) { - is_visible = false; - } - } - - return is_visible; -} - - - - - +// +// KRViewport.cpp +// KREngine +// +// Created by Kearwood Gilbert on 2012-10-25. +// Copyright (c) 2012 Kearwood Software. All rights reserved. +// + +#define KRENGINE_SWAP_INT(x,y) {int t;t=x;x=y;y=t;} + +#include "KREngine-common.h" + +#include "KRViewport.h" + +KRViewport::KRViewport() +{ + m_size = Vector2::One(); + m_matProjection = Matrix4(); + m_matView = Matrix4(); + m_lodBias = 0.0f; + calculateDerivedValues(); +} + +KRViewport::KRViewport(const Vector2 &size, const Matrix4 &matView, const Matrix4 &matProjection) +{ + m_size = size; + m_matView = matView; + m_matProjection = matProjection; + calculateDerivedValues(); +} + + +KRViewport& KRViewport::operator=(const KRViewport &v) { + if(this != &v) { // Prevent self-assignment. + m_size = v.m_size; + m_matProjection = v.m_matProjection; + m_matView = v.m_matView; + m_lodBias = v.m_lodBias; + + calculateDerivedValues(); + } + return *this; +} + + +KRViewport::~KRViewport() +{ + +} + +const Vector2 &KRViewport::getSize() const +{ + return m_size; +} + +const Matrix4 &KRViewport::getViewMatrix() const +{ + return m_matView; +} + +const Matrix4 &KRViewport::getProjectionMatrix() const +{ + return m_matProjection; +} + +void KRViewport::setSize(const Vector2 &size) +{ + m_size = size; +} + +void KRViewport::setViewMatrix(const Matrix4 &matView) +{ + m_matView = matView; + calculateDerivedValues(); +} + +void KRViewport::setProjectionMatrix(const Matrix4 &matProjection) +{ + m_matProjection = matProjection; + calculateDerivedValues(); +} + +const Matrix4 &KRViewport::KRViewport::getViewProjectionMatrix() const +{ + return m_matViewProjection; +} + +const Matrix4 &KRViewport::getInverseViewMatrix() const +{ + return m_matInverseView; +} + +const Matrix4 &KRViewport::getInverseProjectionMatrix() const +{ + return m_matInverseProjection; +} + +const Vector3 &KRViewport::getCameraDirection() const +{ + return m_cameraDirection; +} + +const Vector3 &KRViewport::getCameraPosition() const +{ + return m_cameraPosition; +} + +const int *KRViewport::getFrontToBackOrder() const +{ + return &m_frontToBackOrder[0]; +} + +const int *KRViewport::getBackToFrontOrder() const +{ + return &m_backToFrontOrder[0]; +} + +void KRViewport::calculateDerivedValues() +{ + m_matViewProjection = m_matView * m_matProjection; + m_matInverseView = Matrix4::Invert(m_matView); + m_matInverseProjection = Matrix4::Invert(m_matProjection); + m_cameraPosition = Matrix4::Dot(m_matInverseView, Vector3::Zero()); + m_cameraDirection = Matrix4::Dot(m_matInverseView, Vector3::Create(0.0, 0.0, 1.0)) - Matrix4::Dot(m_matInverseView, Vector3::Create(0.0, 0.0, 0.0)); + + for(int i=0; i<8; i++) { + m_frontToBackOrder[i] = i; + } + + if(m_cameraDirection.x > 0.0) { + KRENGINE_SWAP_INT(m_frontToBackOrder[0], m_frontToBackOrder[1]); + KRENGINE_SWAP_INT(m_frontToBackOrder[2], m_frontToBackOrder[3]); + KRENGINE_SWAP_INT(m_frontToBackOrder[4], m_frontToBackOrder[5]); + KRENGINE_SWAP_INT(m_frontToBackOrder[6], m_frontToBackOrder[7]); + } + + if(m_cameraDirection.y > 0.0) { + KRENGINE_SWAP_INT(m_frontToBackOrder[0], m_frontToBackOrder[2]); + KRENGINE_SWAP_INT(m_frontToBackOrder[1], m_frontToBackOrder[3]); + KRENGINE_SWAP_INT(m_frontToBackOrder[4], m_frontToBackOrder[6]); + KRENGINE_SWAP_INT(m_frontToBackOrder[5], m_frontToBackOrder[7]); + } + + if(m_cameraDirection.z > 0.0) { + KRENGINE_SWAP_INT(m_frontToBackOrder[0], m_frontToBackOrder[4]); + KRENGINE_SWAP_INT(m_frontToBackOrder[1], m_frontToBackOrder[5]); + KRENGINE_SWAP_INT(m_frontToBackOrder[2], m_frontToBackOrder[6]); + KRENGINE_SWAP_INT(m_frontToBackOrder[3], m_frontToBackOrder[7]); + } + + for(int i=0; i<8; i++) { + m_backToFrontOrder[i] = m_frontToBackOrder[7-i]; + } +} + + +unordered_map &KRViewport::getVisibleBounds() +{ + return m_visibleBounds; +} + +float KRViewport::getLODBias() const +{ + return m_lodBias; +} + +void KRViewport::setLODBias(float lod_bias) +{ + m_lodBias = lod_bias; +} + +float KRViewport::coverage(const AABB &b) const +{ + if(!visible(b)) { + return 0.0f; // Culled out by view frustrum + } else { + Vector3 nearest_point = b.nearestPoint(getCameraPosition()); + float distance = (nearest_point - getCameraPosition()).magnitude(); + + Vector3 v = Matrix4::DotWDiv(m_matProjection, getCameraPosition() + getCameraDirection() * distance); + + float screen_depth = distance / 1000.0f; + + return KRCLAMP(1.0f - screen_depth, 0.01f, 1.0f); + + /* + + Vector2 screen_min; + Vector2 screen_max; + // Loop through all corners and transform them to screen space + for(int i=0; i<8; i++) { + Vector3 screen_pos = Matrix4::DotWDiv(m_matViewProjection, Vector3(i & 1 ? b.min.x : b.max.x, i & 2 ? b.min.y : b.max.y, i & 4 ? b.min.z : b.max.z)); + if(i==0) { + screen_min = screen_pos.xy(); + screen_max = screen_pos.xy(); + } else { + if(screen_pos.x < screen_min.x) screen_min.x = screen_pos.x; + if(screen_pos.y < screen_min.y) screen_min.y = screen_pos.y; + if(screen_pos.x > screen_max.x) screen_max.x = screen_pos.x; + if(screen_pos.y > screen_max.y) screen_max.y = screen_pos.y; + } + } + + screen_min.x = KRCLAMP(screen_min.x, 0.0f, 1.0f); + screen_min.y = KRCLAMP(screen_min.y, 0.0f, 1.0f); + screen_max.x = KRCLAMP(screen_max.x, 0.0f, 1.0f); + screen_max.y = KRCLAMP(screen_max.y, 0.0f, 1.0f); + + float c = (screen_max.x - screen_min.x) * (screen_max.y - screen_min.y); + return KRCLAMP(c, 0.01f, 1.0f); + */ + } +} + + +bool KRViewport::visible(const AABB &b) const +{ + // test if bounding box would be within the visible range of the clip space transformed by matViewProjection + // This is used for view frustrum culling + + int outside_count[6] = {0, 0, 0, 0, 0, 0}; + + for(int iCorner=0; iCorner<8; iCorner++) { + Vector4 sourceCornerVertex = Vector4::Create( + (iCorner & 1) == 0 ? b.min.x : b.max.x, + (iCorner & 2) == 0 ? b.min.y : b.max.y, + (iCorner & 4) == 0 ? b.min.z : b.max.z, 1.0f); + + Vector4 cornerVertex = Matrix4::Dot4(m_matViewProjection, sourceCornerVertex); + + if(cornerVertex.x < -cornerVertex.w) { + outside_count[0]++; + } + if(cornerVertex.y < -cornerVertex.w) { + outside_count[1]++; + } + if(cornerVertex.z < -cornerVertex.w) { + outside_count[2]++; + } + if(cornerVertex.x > cornerVertex.w) { + outside_count[3]++; + } + if(cornerVertex.y > cornerVertex.w) { + outside_count[4]++; + } + if(cornerVertex.z > cornerVertex.w) { + outside_count[5]++; + } + } + + bool is_visible = true; + for(int iFace=0; iFace < 6; iFace++) { + if(outside_count[iFace] == 8) { + is_visible = false; + } + } + + return is_visible; +} + + + + + diff --git a/kraken/KRViewport.h b/kraken/KRViewport.h index f8baa09..1e98307 100755 --- a/kraken/KRViewport.h +++ b/kraken/KRViewport.h @@ -1,73 +1,75 @@ -// -// KRViewport.h -// KREngine -// -// Created by Kearwood Gilbert on 2012-10-25. -// Copyright (c) 2012 Kearwood Software. All rights reserved. -// - -#ifndef KRENGINE_KRVIEWPORT_H -#define KRENGINE_KRVIEWPORT_H - -#include "KREngine-common.h" - -class KRLight; - -class KRViewport { -public: - KRViewport(); - KRViewport(const Vector2 &size, const Matrix4 &matView, const Matrix4 &matProjection); - ~KRViewport(); - - const Vector2 &getSize() const; - const Matrix4 &getViewMatrix() const; - const Matrix4 &getProjectionMatrix() const; - const Matrix4 &getViewProjectionMatrix() const; - const Matrix4 &getInverseViewMatrix() const; - const Matrix4 &getInverseProjectionMatrix() const; - const Vector3 &getCameraDirection() const; - const Vector3 &getCameraPosition() const; - const int *getFrontToBackOrder() const; - const int *getBackToFrontOrder() const; - void setSize(const Vector2 &size); - void setViewMatrix(const Matrix4 &matView); - void setProjectionMatrix(const Matrix4 &matProjection); - float getLODBias() const; - void setLODBias(float lod_bias); - - // Overload assignment operator - KRViewport& operator=(const KRViewport &v); - - unordered_map &getVisibleBounds(); - - const std::set &getVisibleLights(); - void setVisibleLights(const std::set visibleLights); - - bool visible(const AABB &b) const; - float coverage(const AABB &b) const; - -private: - Vector2 m_size; - Matrix4 m_matView; - Matrix4 m_matProjection; - - float m_lodBias; - - // Derived values - Matrix4 m_matViewProjection; - Matrix4 m_matInverseView; - Matrix4 m_matInverseProjection; - Vector3 m_cameraDirection; - Vector3 m_cameraPosition; - - int m_frontToBackOrder[8]; - int m_backToFrontOrder[8]; - - void calculateDerivedValues(); - - unordered_map m_visibleBounds; // AABB's that output fragments in the last frame - - -}; - -#endif +// +// KRViewport.h +// KREngine +// +// Created by Kearwood Gilbert on 2012-10-25. +// Copyright (c) 2012 Kearwood Software. All rights reserved. +// + +#ifndef KRENGINE_KRVIEWPORT_H +#define KRENGINE_KRVIEWPORT_H + +#include "KREngine-common.h" + +#include "aabb.h" + +class KRLight; + +class KRViewport { +public: + KRViewport(); + KRViewport(const Vector2 &size, const Matrix4 &matView, const Matrix4 &matProjection); + ~KRViewport(); + + const Vector2 &getSize() const; + const Matrix4 &getViewMatrix() const; + const Matrix4 &getProjectionMatrix() const; + const Matrix4 &getViewProjectionMatrix() const; + const Matrix4 &getInverseViewMatrix() const; + const Matrix4 &getInverseProjectionMatrix() const; + const Vector3 &getCameraDirection() const; + const Vector3 &getCameraPosition() const; + const int *getFrontToBackOrder() const; + const int *getBackToFrontOrder() const; + void setSize(const Vector2 &size); + void setViewMatrix(const Matrix4 &matView); + void setProjectionMatrix(const Matrix4 &matProjection); + float getLODBias() const; + void setLODBias(float lod_bias); + + // Overload assignment operator + KRViewport& operator=(const KRViewport &v); + + unordered_map &getVisibleBounds(); + + const std::set &getVisibleLights(); + void setVisibleLights(const std::set visibleLights); + + bool visible(const AABB &b) const; + float coverage(const AABB &b) const; + +private: + Vector2 m_size; + Matrix4 m_matView; + Matrix4 m_matProjection; + + float m_lodBias; + + // Derived values + Matrix4 m_matViewProjection; + Matrix4 m_matInverseView; + Matrix4 m_matInverseProjection; + Vector3 m_cameraDirection; + Vector3 m_cameraPosition; + + int m_frontToBackOrder[8]; + int m_backToFrontOrder[8]; + + void calculateDerivedValues(); + + unordered_map m_visibleBounds; // AABB's that output fragments in the last frame + + +}; + +#endif diff --git a/kraken/aabb.cpp b/kraken/aabb.cpp deleted file mode 100644 index 68e4316..0000000 --- a/kraken/aabb.cpp +++ /dev/null @@ -1,339 +0,0 @@ -// -// KRAABB.cpp -// KREngine -// -// Created by Kearwood Gilbert on 2012-08-30. -// Copyright (c) 2012 Kearwood Software. All rights reserved. -// - -#include "public/kraken.h" -#include "assert.h" -#include "KRHelpers.h" - -namespace kraken { - -AABB::AABB() -{ - min = Vector3::Min(); - max = Vector3::Max(); -} - -AABB::AABB(const Vector3 &minPoint, const Vector3 &maxPoint) -{ - min = minPoint; - max = maxPoint; -} - -AABB::AABB(const Vector3 &corner1, const Vector3 &corner2, const Matrix4 &modelMatrix) -{ - for(int iCorner=0; iCorner<8; iCorner++) { - Vector3 sourceCornerVertex = Matrix4::DotWDiv(modelMatrix, Vector3( - (iCorner & 1) == 0 ? corner1.x : corner2.x, - (iCorner & 2) == 0 ? corner1.y : corner2.y, - (iCorner & 4) == 0 ? corner1.z : corner2.z)); - - - if(iCorner == 0) { - min = sourceCornerVertex; - max = sourceCornerVertex; - } else { - if(sourceCornerVertex.x < min.x) min.x = sourceCornerVertex.x; - if(sourceCornerVertex.y < min.y) min.y = sourceCornerVertex.y; - if(sourceCornerVertex.z < min.z) min.z = sourceCornerVertex.z; - if(sourceCornerVertex.x > max.x) max.x = sourceCornerVertex.x; - if(sourceCornerVertex.y > max.y) max.y = sourceCornerVertex.y; - if(sourceCornerVertex.z > max.z) max.z = sourceCornerVertex.z; - } - } -} - -AABB::~AABB() -{ - -} - -AABB& AABB::operator =(const AABB& b) -{ - min = b.min; - max = b.max; - - return *this; -} - -bool AABB::operator ==(const AABB& b) const -{ - return min == b.min && max == b.max; -} - -bool AABB::operator !=(const AABB& b) const -{ - return min != b.min || max != b.max; -} - -Vector3 AABB::center() const -{ - return (min + max) * 0.5f; -} - -Vector3 AABB::size() const -{ - return max - min; -} - -float AABB::volume() const -{ - Vector3 s = size(); - return s.x * s.y * s.z; -} - -void AABB::scale(const Vector3 &s) -{ - Vector3 prev_center = center(); - Vector3 prev_size = size(); - Vector3 new_scale = Vector3(prev_size.x * s.x, prev_size.y * s.y, prev_size.z * s.z) * 0.5f; - min = prev_center - new_scale; - max = prev_center + new_scale; -} - -void AABB::scale(float s) -{ - scale(Vector3(s)); -} - -bool AABB::operator >(const AABB& b) const -{ - // Comparison operators are implemented to allow insertion into sorted containers such as std::set - if(min > b.min) { - return true; - } else if(min < b.min) { - return false; - } else if(max > b.max) { - return true; - } else { - return false; - } - -} -bool AABB::operator <(const AABB& b) const -{ - // Comparison operators are implemented to allow insertion into sorted containers such as std::set - - if(min < b.min) { - return true; - } else if(min > b.min) { - return false; - } else if(max < b.max) { - return true; - } else { - return false; - } -} - -bool AABB::intersects(const AABB& b) const -{ - // Return true if the two volumes intersect - return min.x <= b.max.x && min.y <= b.max.y && min.z <= b.max.z && max.x >= b.min.x && max.y >= b.min.y && max.z >= b.min.z; -} - -bool AABB::contains(const AABB &b) const -{ - // Return true if the passed KRAABB is entirely contained within this KRAABB - return b.min.x >= min.x && b.min.y >= min.y && b.min.z >= min.z && b.max.x <= max.x && b.max.y <= max.y && b.max.z <= max.z; -} - -bool AABB::contains(const Vector3 &v) const -{ - return v.x >= min.x && v.x <= max.x && v.y >= min.y && v.y <= max.y && v.z >= min.z && v.z <= max.z; -} - -AABB AABB::Infinite() -{ - return AABB(Vector3::Min(), Vector3::Max()); -} - -AABB AABB::Zero() -{ - return AABB(Vector3::Zero(), Vector3::Zero()); -} - -float AABB::longest_radius() const -{ - float radius1 = (center() - min).magnitude(); - float radius2 = (max - center()).magnitude(); - return radius1 > radius2 ? radius1 : radius2; -} - - -bool AABB::intersectsLine(const Vector3 &v1, const Vector3 &v2) const -{ - Vector3 dir = Vector3::Normalize(v2 - v1); - float length = (v2 - v1).magnitude(); - - // EZ cases: if the ray starts inside the box, or ends inside - // the box, then it definitely hits the box. - // I'm using this code for ray tracing with an octree, - // so I needed rays that start and end within an - // octree node to COUNT as hits. - // You could modify this test to (ray starts inside and ends outside) - // to qualify as a hit if you wanted to NOT count totally internal rays - if( contains( v1 ) || contains( v2 ) ) - return true ; - - // the algorithm says, find 3 t's, - Vector3 t ; - - // LARGEST t is the only one we need to test if it's on the face. - for(int i = 0 ; i < 3 ; i++) { - if( dir[i] > 0 ) { // CULL BACK FACE - t[i] = ( min[i] - v1[i] ) / dir[i]; - } else { - t[i] = ( max[i] - v1[i] ) / dir[i]; - } - } - - int mi = 0; - if(t[1] > t[mi]) mi = 1; - if(t[2] > t[mi]) mi = 2; - if(t[mi] >= 0 && t[mi] <= length) { - Vector3 pt = v1 + dir * t[mi]; - - // check it's in the box in other 2 dimensions - int o1 = ( mi + 1 ) % 3 ; // i=0: o1=1, o2=2, i=1: o1=2,o2=0 etc. - int o2 = ( mi + 2 ) % 3 ; - - return pt[o1] >= min[o1] && pt[o1] <= max[o1] && pt[o2] >= min[o2] && pt[o2] <= max[o2]; - } - - return false ; // the ray did not hit the box. -} - -bool AABB::intersectsRay(const Vector3 &v1, const Vector3 &dir) const -{ - /* - Fast Ray-Box Intersection - by Andrew Woo - from "Graphics Gems", Academic Press, 1990 - */ - - // FINDME, TODO - Perhaps there is a more efficient algorithm, as we don't actually need the exact coordinate of the intersection - - enum { - RIGHT = 0, - LEFT = 1, - MIDDLE = 2 - } quadrant[3]; - - bool inside = true; - Vector3 maxT; - Vector3 coord; - double candidatePlane[3]; - - // Find candidate planes; this loop can be avoided if rays cast all from the eye(assume perpsective view) - for (int i=0; i<3; i++) - if(v1.c[i] < min.c[i]) { - quadrant[i] = LEFT; - candidatePlane[i] = min.c[i]; - inside = false; - } else if(v1.c[i] > max.c[i]) { - quadrant[i] = RIGHT; - candidatePlane[i] = max.c[i]; - inside = false; - } else { - quadrant[i] = MIDDLE; - } - - /* Ray v1 inside bounding box */ - if (inside) { - coord = v1; - return true; - } - - /* Calculate T distances to candidate planes */ - for (int i = 0; i < 3; i++) { - if (quadrant[i] != MIDDLE && dir[i] != 0.0f) { - maxT.c[i] = (candidatePlane[i]-v1.c[i]) / dir[i]; - } else { - maxT.c[i] = -1.0f; - } - } - - /* Get largest of the maxT's for final choice of intersection */ - int whichPlane = 0; - for (int i = 1; i < 3; i++) { - if (maxT.c[whichPlane] < maxT.c[i]) { - whichPlane = i; - } - } - - /* Check final candidate actually inside box */ - if (maxT.c[whichPlane] < 0.0f) { - return false; - } - for (int i = 0; i < 3; i++) { - if (whichPlane != i) { - coord[i] = v1.c[i] + maxT.c[whichPlane] *dir[i]; - if (coord[i] < min.c[i] || coord[i] > max.c[i]) { - return false; - } - } else { - assert(quadrant[i] != MIDDLE); // This should not be possible - coord[i] = candidatePlane[i]; - } - } - return true; /* ray hits box */ -} - -bool AABB::intersectsSphere(const Vector3 ¢er, float radius) const -{ - // Arvo's Algorithm - - float squaredDistance = 0; - - // process X - if (center.x < min.x) { - float diff = center.x - min.x; - squaredDistance += diff * diff; - } else if (center.x > max.x) { - float diff = center.x - max.x; - squaredDistance += diff * diff; - } - - // process Y - if (center.y < min.y) { - float diff = center.y - min.y; - squaredDistance += diff * diff; - } else if (center.y > max.y) { - float diff = center.y - max.y; - squaredDistance += diff * diff; - } - - // process Z - if (center.z < min.z) { - float diff = center.z - min.z; - squaredDistance += diff * diff; - } else if (center.z > max.z) { - float diff = center.z - max.z; - squaredDistance += diff * diff; - } - - return squaredDistance <= radius; -} - -void AABB::encapsulate(const AABB & b) -{ - if(b.min.x < min.x) min.x = b.min.x; - if(b.min.y < min.y) min.y = b.min.y; - if(b.min.z < min.z) min.z = b.min.z; - - if(b.max.x > max.x) max.x = b.max.x; - if(b.max.y > max.y) max.y = b.max.y; - if(b.max.z > max.z) max.z = b.max.z; -} - -Vector3 AABB::nearestPoint(const Vector3 & v) const -{ - return Vector3(KRCLAMP(v.x, min.x, max.x), KRCLAMP(v.y, min.y, max.y), KRCLAMP(v.z, min.z, max.z)); -} - -} // namespace kraken - diff --git a/kraken/hitinfo.cpp b/kraken/hitinfo.cpp deleted file mode 100755 index b5eb505..0000000 --- a/kraken/hitinfo.cpp +++ /dev/null @@ -1,100 +0,0 @@ -// -// HitInfo.cpp -// KREngine -// -// Copyright 2012 Kearwood Gilbert. All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, are -// permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of -// conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list -// of conditions and the following disclaimer in the documentation and/or other materials -// provided with the distribution. -// -// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED -// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR -// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// The views and conclusions contained in the software and documentation are those of the -// authors and should not be interpreted as representing official policies, either expressed -// or implied, of Kearwood Gilbert. -// - -#include "public/kraken.h" - -namespace kraken { - -HitInfo::HitInfo() -{ - m_position = Vector3::Zero(); - m_normal = Vector3::Zero(); - m_distance = 0.0f; - m_node = NULL; -} - -HitInfo::HitInfo(const Vector3 &position, const Vector3 &normal, const float distance, KRNode *node) -{ - m_position = position; - m_normal = normal; - m_distance = distance; - m_node = node; -} - -HitInfo::HitInfo(const Vector3 &position, const Vector3 &normal, const float distance) -{ - m_position = position; - m_normal = normal; - m_distance = distance; - m_node = NULL; -} - -HitInfo::~HitInfo() -{ - -} - -bool HitInfo::didHit() const -{ - return m_normal != Vector3::Zero(); -} - -Vector3 HitInfo::getPosition() const -{ - return m_position; -} - -Vector3 HitInfo::getNormal() const -{ - return m_normal; -} - -float HitInfo::getDistance() const -{ - return m_distance; -} - -KRNode *HitInfo::getNode() const -{ - return m_node; -} - -HitInfo& HitInfo::operator =(const HitInfo& b) -{ - m_position = b.m_position; - m_normal = b.m_normal; - m_distance = b.m_distance; - m_node = b.m_node; - return *this; -} - -} // namespace kraken - diff --git a/kraken/matrix4.cpp b/kraken/matrix4.cpp deleted file mode 100644 index 4021e62..0000000 --- a/kraken/matrix4.cpp +++ /dev/null @@ -1,448 +0,0 @@ -// -// Matrix4.cpp -// KREngine -// -// Copyright 2012 Kearwood Gilbert. All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, are -// permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of -// conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list -// of conditions and the following disclaimer in the documentation and/or other materials -// provided with the distribution. -// -// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED -// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR -// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// The views and conclusions contained in the software and documentation are those of the -// authors and should not be interpreted as representing official policies, either expressed -// or implied, of Kearwood Gilbert. -// - -#include "public/kraken.h" -#include - -namespace kraken { - -Matrix4::Matrix4() { - // Default constructor - Initialize with an identity matrix - static const float IDENTITY_MATRIX[] = { - 1.0, 0.0, 0.0, 0.0, - 0.0, 1.0, 0.0, 0.0, - 0.0, 0.0, 1.0, 0.0, - 0.0, 0.0, 0.0, 1.0 - }; - memcpy(c, IDENTITY_MATRIX, sizeof(float) * 16); - -} - -Matrix4::Matrix4(float *pMat) { - memcpy(c, pMat, sizeof(float) * 16); -} - -Matrix4::Matrix4(const Vector3 &new_axis_x, const Vector3 &new_axis_y, const Vector3 &new_axis_z, const Vector3 &new_transform) -{ - c[0] = new_axis_x.x; c[1] = new_axis_x.y; c[2] = new_axis_x.z; c[3] = 0.0f; - c[4] = new_axis_y.x; c[5] = new_axis_y.y; c[6] = new_axis_y.z; c[7] = 0.0f; - c[8] = new_axis_z.x; c[9] = new_axis_z.y; c[10] = new_axis_z.z; c[11] = 0.0f; - c[12] = new_transform.x; c[13] = new_transform.y; c[14] = new_transform.z; c[15] = 1.0f; -} - -Matrix4::~Matrix4() { - -} - -float *Matrix4::getPointer() { - return c; -} - -// Copy constructor -Matrix4::Matrix4(const Matrix4 &m) { - memcpy(c, m.c, sizeof(float) * 16); -} - -Matrix4& Matrix4::operator=(const Matrix4 &m) { - if(this != &m) { // Prevent self-assignment. - memcpy(c, m.c, sizeof(float) * 16); - } - return *this; -} - -float& Matrix4::operator[](unsigned i) { - return c[i]; -} - -float Matrix4::operator[](unsigned i) const { - return c[i]; -} - -// Overload comparison operator -bool Matrix4::operator==(const Matrix4 &m) const { - return memcmp(c, m.c, sizeof(float) * 16) == 0; -} - -// Overload compound multiply operator -Matrix4& Matrix4::operator*=(const Matrix4 &m) { - float temp[16]; - - int x,y; - - for (x=0; x < 4; x++) - { - for(y=0; y < 4; y++) - { - temp[y + (x*4)] = (c[x*4] * m.c[y]) + - (c[(x*4)+1] * m.c[y+4]) + - (c[(x*4)+2] * m.c[y+8]) + - (c[(x*4)+3] * m.c[y+12]); - } - } - - memcpy(c, temp, sizeof(float) << 4); - return *this; -} - -// Overload multiply operator -Matrix4 Matrix4::operator*(const Matrix4 &m) const { - Matrix4 ret = *this; - ret *= m; - return ret; -} - - -/* Generate a perspective view matrix using a field of view angle fov, - * window aspect ratio, near and far clipping planes */ -void Matrix4::perspective(float fov, float aspect, float nearz, float farz) { - - memset(c, 0, sizeof(float) * 16); - - float range= tan(fov * 0.5) * nearz; - c[0] = (2 * nearz) / ((range * aspect) - (-range * aspect)); - c[5] = (2 * nearz) / (2 * range); - c[10] = -(farz + nearz) / (farz - nearz); - c[11] = -1; - c[14] = -(2 * farz * nearz) / (farz - nearz); - /* - float range= atan(fov / 20.0f) * nearz; - float r = range * aspect; - float t = range * 1.0; - - c[0] = nearz / r; - c[5] = nearz / t; - c[10] = -(farz + nearz) / (farz - nearz); - c[11] = -(2.0 * farz * nearz) / (farz - nearz); - c[14] = -1.0; - */ -} - -/* Perform translation operations on a matrix */ -void Matrix4::translate(float x, float y, float z) { - Matrix4 newMatrix; // Create new identity matrix - - newMatrix.c[12] = x; - newMatrix.c[13] = y; - newMatrix.c[14] = z; - - *this *= newMatrix; -} - -void Matrix4::translate(const Vector3 &v) -{ - translate(v.x, v.y, v.z); -} - -/* Rotate a matrix by an angle on a X, Y, or Z axis */ -void Matrix4::rotate(float angle, AXIS axis) { - const int cos1[3] = { 5, 0, 0 }; // cos(angle) - const int cos2[3] = { 10, 10, 5 }; // cos(angle) - const int sin1[3] = { 9, 2, 4 }; // -sin(angle) - const int sin2[3] = { 6, 8, 1 }; // sin(angle) - - /* - X_AXIS: - - 1, 0, 0, 0 - 0, cos(angle), -sin(angle), 0 - 0, sin(angle), cos(angle), 0 - 0, 0, 0, 1 - - Y_AXIS: - - cos(angle), 0, -sin(angle), 0 - 0, 1, 0, 0 - sin(angle), 0, cos(angle), 0 - 0, 0, 0, 1 - - Z_AXIS: - - cos(angle), -sin(angle), 0, 0 - sin(angle), cos(angle), 0, 0 - 0, 0, 1, 0 - 0, 0, 0, 1 - - */ - - Matrix4 newMatrix; // Create new identity matrix - - newMatrix.c[cos1[axis]] = cos(angle); - newMatrix.c[sin1[axis]] = -sin(angle); - newMatrix.c[sin2[axis]] = -newMatrix.c[sin1[axis]]; - newMatrix.c[cos2[axis]] = newMatrix.c[cos1[axis]]; - - *this *= newMatrix; -} - -void Matrix4::rotate(const Quaternion &q) -{ - *this *= q.rotationMatrix(); -} - -/* Scale matrix by separate x, y, and z amounts */ -void Matrix4::scale(float x, float y, float z) { - Matrix4 newMatrix; // Create new identity matrix - - newMatrix.c[0] = x; - newMatrix.c[5] = y; - newMatrix.c[10] = z; - - *this *= newMatrix; -} - -void Matrix4::scale(const Vector3 &v) { - scale(v.x, v.y, v.z); -} - -/* Scale all dimensions equally */ -void Matrix4::scale(float s) { - scale(s,s,s); -} - - // Initialize with a bias matrix -void Matrix4::bias() { - static const float BIAS_MATRIX[] = { - 0.5, 0.0, 0.0, 0.0, - 0.0, 0.5, 0.0, 0.0, - 0.0, 0.0, 0.5, 0.0, - 0.5, 0.5, 0.5, 1.0 - }; - memcpy(c, BIAS_MATRIX, sizeof(float) * 16); -} - - -/* Generate an orthographic view matrix */ -void Matrix4::ortho(float left, float right, float top, float bottom, float nearz, float farz) { - memset(c, 0, sizeof(float) * 16); - c[0] = 2.0f / (right - left); - c[5] = 2.0f / (bottom - top); - c[10] = -1.0f / (farz - nearz); - c[11] = -nearz / (farz - nearz); - c[15] = 1.0f; -} - -/* Replace matrix with its inverse */ -bool Matrix4::invert() { - // Based on gluInvertMatrix implementation - - float inv[16], det; - int i; - - inv[0] = c[5]*c[10]*c[15] - c[5]*c[11]*c[14] - c[9]*c[6]*c[15] - + c[9]*c[7]*c[14] + c[13]*c[6]*c[11] - c[13]*c[7]*c[10]; - inv[4] = -c[4]*c[10]*c[15] + c[4]*c[11]*c[14] + c[8]*c[6]*c[15] - - c[8]*c[7]*c[14] - c[12]*c[6]*c[11] + c[12]*c[7]*c[10]; - inv[8] = c[4]*c[9]*c[15] - c[4]*c[11]*c[13] - c[8]*c[5]*c[15] - + c[8]*c[7]*c[13] + c[12]*c[5]*c[11] - c[12]*c[7]*c[9]; - inv[12] = -c[4]*c[9]*c[14] + c[4]*c[10]*c[13] + c[8]*c[5]*c[14] - - c[8]*c[6]*c[13] - c[12]*c[5]*c[10] + c[12]*c[6]*c[9]; - inv[1] = -c[1]*c[10]*c[15] + c[1]*c[11]*c[14] + c[9]*c[2]*c[15] - - c[9]*c[3]*c[14] - c[13]*c[2]*c[11] + c[13]*c[3]*c[10]; - inv[5] = c[0]*c[10]*c[15] - c[0]*c[11]*c[14] - c[8]*c[2]*c[15] - + c[8]*c[3]*c[14] + c[12]*c[2]*c[11] - c[12]*c[3]*c[10]; - inv[9] = -c[0]*c[9]*c[15] + c[0]*c[11]*c[13] + c[8]*c[1]*c[15] - - c[8]*c[3]*c[13] - c[12]*c[1]*c[11] + c[12]*c[3]*c[9]; - inv[13] = c[0]*c[9]*c[14] - c[0]*c[10]*c[13] - c[8]*c[1]*c[14] - + c[8]*c[2]*c[13] + c[12]*c[1]*c[10] - c[12]*c[2]*c[9]; - inv[2] = c[1]*c[6]*c[15] - c[1]*c[7]*c[14] - c[5]*c[2]*c[15] - + c[5]*c[3]*c[14] + c[13]*c[2]*c[7] - c[13]*c[3]*c[6]; - inv[6] = -c[0]*c[6]*c[15] + c[0]*c[7]*c[14] + c[4]*c[2]*c[15] - - c[4]*c[3]*c[14] - c[12]*c[2]*c[7] + c[12]*c[3]*c[6]; - inv[10] = c[0]*c[5]*c[15] - c[0]*c[7]*c[13] - c[4]*c[1]*c[15] - + c[4]*c[3]*c[13] + c[12]*c[1]*c[7] - c[12]*c[3]*c[5]; - inv[14] = -c[0]*c[5]*c[14] + c[0]*c[6]*c[13] + c[4]*c[1]*c[14] - - c[4]*c[2]*c[13] - c[12]*c[1]*c[6] + c[12]*c[2]*c[5]; - inv[3] = -c[1]*c[6]*c[11] + c[1]*c[7]*c[10] + c[5]*c[2]*c[11] - - c[5]*c[3]*c[10] - c[9]*c[2]*c[7] + c[9]*c[3]*c[6]; - inv[7] = c[0]*c[6]*c[11] - c[0]*c[7]*c[10] - c[4]*c[2]*c[11] - + c[4]*c[3]*c[10] + c[8]*c[2]*c[7] - c[8]*c[3]*c[6]; - inv[11] = -c[0]*c[5]*c[11] + c[0]*c[7]*c[9] + c[4]*c[1]*c[11] - - c[4]*c[3]*c[9] - c[8]*c[1]*c[7] + c[8]*c[3]*c[5]; - inv[15] = c[0]*c[5]*c[10] - c[0]*c[6]*c[9] - c[4]*c[1]*c[10] - + c[4]*c[2]*c[9] + c[8]*c[1]*c[6] - c[8]*c[2]*c[5]; - - det = c[0]*inv[0] + c[1]*inv[4] + c[2]*inv[8] + c[3]*inv[12]; - - if (det == 0) { - return false; - } - - det = 1.0 / det; - - for (i = 0; i < 16; i++) { - c[i] = inv[i] * det; - } - - return true; -} - -void Matrix4::transpose() { - float trans[16]; - for(int x=0; x<4; x++) { - for(int y=0; y<4; y++) { - trans[x + y * 4] = c[y + x * 4]; - } - } - memcpy(c, trans, sizeof(float) * 16); -} - -/* Dot Product, returning Vector3 */ -Vector3 Matrix4::Dot(const Matrix4 &m, const Vector3 &v) { - return Vector3( - v.c[0] * m.c[0] + v.c[1] * m.c[4] + v.c[2] * m.c[8] + m.c[12], - v.c[0] * m.c[1] + v.c[1] * m.c[5] + v.c[2] * m.c[9] + m.c[13], - v.c[0] * m.c[2] + v.c[1] * m.c[6] + v.c[2] * m.c[10] + m.c[14] - ); -} - -Vector4 Matrix4::Dot4(const Matrix4 &m, const Vector4 &v) { -#ifdef KRAKEN_USE_ARM_NEON - - Vector4 d; - asm volatile ( - "vld1.32 {d0, d1}, [%1] \n\t" //Q0 = v - "vld1.32 {d18, d19}, [%0]! \n\t" //Q1 = m - "vld1.32 {d20, d21}, [%0]! \n\t" //Q2 = m+4 - "vld1.32 {d22, d23}, [%0]! \n\t" //Q3 = m+8 - "vld1.32 {d24, d25}, [%0]! \n\t" //Q4 = m+12 - - "vmul.f32 q13, q9, d0[0] \n\t" //Q5 = Q1*Q0[0] - "vmla.f32 q13, q10, d0[1] \n\t" //Q5 += Q1*Q0[1] - "vmla.f32 q13, q11, d1[0] \n\t" //Q5 += Q2*Q0[2] - "vmla.f32 q13, q12, d1[1] \n\t" //Q5 += Q3*Q0[3] - - "vst1.32 {d26, d27}, [%2] \n\t" //Q4 = m+12 - : /* no output registers */ - : "r"(m.c), "r"(v.c), "r"(d.c) - : "q0", "q9", "q10","q11", "q12", "q13", "memory" - ); - return d; -#else - return Vector4( - v.c[0] * m.c[0] + v.c[1] * m.c[4] + v.c[2] * m.c[8] + m.c[12], - v.c[0] * m.c[1] + v.c[1] * m.c[5] + v.c[2] * m.c[9] + m.c[13], - v.c[0] * m.c[2] + v.c[1] * m.c[6] + v.c[2] * m.c[10] + m.c[14], - v.c[0] * m.c[3] + v.c[1] * m.c[7] + v.c[2] * m.c[11] + m.c[15] - ); -#endif -} - -// Dot product without including translation; useful for transforming normals and tangents -Vector3 Matrix4::DotNoTranslate(const Matrix4 &m, const Vector3 &v) -{ - return Vector3( - v.x * m.c[0] + v.y * m.c[4] + v.z * m.c[8], - v.x * m.c[1] + v.y * m.c[5] + v.z * m.c[9], - v.x * m.c[2] + v.y * m.c[6] + v.z * m.c[10] - ); -} - -/* Dot Product, returning w component as if it were a Vector4 (This will be deprecated once Vector4 is implemented instead*/ -float Matrix4::DotW(const Matrix4 &m, const Vector3 &v) { - return v.x * m.c[0*4 + 3] + v.y * m.c[1*4 + 3] + v.z * m.c[2*4 + 3] + m.c[3*4 + 3]; -} - -/* Dot Product followed by W-divide */ -Vector3 Matrix4::DotWDiv(const Matrix4 &m, const Vector3 &v) { - Vector4 r = Dot4(m, Vector4(v, 1.0f)); - return Vector3(r) / r.w; -} - -Matrix4 Matrix4::LookAt(const Vector3 &cameraPos, const Vector3 &lookAtPos, const Vector3 &upDirection) -{ - Matrix4 matLookat; - Vector3 lookat_z_axis = lookAtPos - cameraPos; - lookat_z_axis.normalize(); - Vector3 lookat_x_axis = Vector3::Cross(upDirection, lookat_z_axis); - lookat_x_axis.normalize(); - Vector3 lookat_y_axis = Vector3::Cross(lookat_z_axis, lookat_x_axis); - - matLookat.getPointer()[0] = lookat_x_axis.x; - matLookat.getPointer()[1] = lookat_y_axis.x; - matLookat.getPointer()[2] = lookat_z_axis.x; - - matLookat.getPointer()[4] = lookat_x_axis.y; - matLookat.getPointer()[5] = lookat_y_axis.y; - matLookat.getPointer()[6] = lookat_z_axis.y; - - matLookat.getPointer()[8] = lookat_x_axis.z; - matLookat.getPointer()[9] = lookat_y_axis.z; - matLookat.getPointer()[10] = lookat_z_axis.z; - - matLookat.getPointer()[12] = -Vector3::Dot(lookat_x_axis, cameraPos); - matLookat.getPointer()[13] = -Vector3::Dot(lookat_y_axis, cameraPos); - matLookat.getPointer()[14] = -Vector3::Dot(lookat_z_axis, cameraPos); - - return matLookat; -} - -Matrix4 Matrix4::Invert(const Matrix4 &m) -{ - Matrix4 matInvert = m; - matInvert.invert(); - return matInvert; -} - -Matrix4 Matrix4::Transpose(const Matrix4 &m) -{ - Matrix4 matTranspose = m; - matTranspose.transpose(); - return matTranspose; -} - -Matrix4 Matrix4::Translation(const Vector3 &v) -{ - Matrix4 m; - m[12] = v.x; - m[13] = v.y; - m[14] = v.z; -// m.translate(v); - return m; -} - -Matrix4 Matrix4::Rotation(const Vector3 &v) -{ - Matrix4 m; - m.rotate(v.x, X_AXIS); - m.rotate(v.y, Y_AXIS); - m.rotate(v.z, Z_AXIS); - return m; -} - -Matrix4 Matrix4::Scaling(const Vector3 &v) -{ - Matrix4 m; - m.scale(v); - return m; -} - -} // namespace kraken - diff --git a/kraken/public/CMakeLists.txt b/kraken/public/CMakeLists.txt index 292c603..ece9c06 100644 --- a/kraken/public/CMakeLists.txt +++ b/kraken/public/CMakeLists.txt @@ -1,11 +1,2 @@ -add_public_header(kraken.h) -add_public_header(scalar.h) -add_public_header(vector2.h) -add_public_header(vector3.h) -add_public_header(vector4.h) -add_public_header(triangle3.h) -add_public_header(quaternion.h) -add_public_header(aabb.h) -add_public_header(matrix4.h) -add_public_header(hitinfo.h) -set(KRAKEN_PUBLIC_HEADERS "${KRAKEN_PUBLIC_HEADERS}" PARENT_SCOPE) +add_public_header(kraken.h) +set(KRAKEN_PUBLIC_HEADERS "${KRAKEN_PUBLIC_HEADERS}" PARENT_SCOPE) diff --git a/kraken/public/aabb.h b/kraken/public/aabb.h deleted file mode 100644 index ee1a056..0000000 --- a/kraken/public/aabb.h +++ /dev/null @@ -1,102 +0,0 @@ -// -// KRAABB.h -// Kraken -// -// Copyright 2018 Kearwood Gilbert. All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, are -// permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of -// conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list -// of conditions and the following disclaimer in the documentation and/or other materials -// provided with the distribution. -// -// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED -// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR -// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// The views and conclusions contained in the software and documentation are those of the -// authors and should not be interpreted as representing official policies, either expressed -// or implied, of Kearwood Gilbert. -// - -// Axis aligned bounding box (AABB) - -#ifndef KRAKEN_AABB_H -#define KRAKEN_AABB_H - -#include // for hash<> - -#include "vector2.h" -#include "vector3.h" - -namespace kraken { - -class Matrix4; - -class AABB { -public: - Vector3 min; - Vector3 max; - - AABB(const Vector3 &minPoint, const Vector3 &maxPoint); - AABB(const Vector3 &corner1, const Vector3 &corner2, const Matrix4 &modelMatrix); - AABB(); - ~AABB(); - - void scale(const Vector3 &s); - void scale(float s); - - Vector3 center() const; - Vector3 size() const; - float volume() const; - bool intersects(const AABB& b) const; - bool contains(const AABB &b) const; - bool contains(const Vector3 &v) const; - - bool intersectsLine(const Vector3 &v1, const Vector3 &v2) const; - bool intersectsRay(const Vector3 &v1, const Vector3 &dir) const; - bool intersectsSphere(const Vector3 ¢er, float radius) const; - void encapsulate(const AABB & b); - - AABB& operator =(const AABB& b); - bool operator ==(const AABB& b) const; - bool operator !=(const AABB& b) const; - - // Comparison operators are implemented to allow insertion into sorted containers such as std::set - bool operator >(const AABB& b) const; - bool operator <(const AABB& b) const; - - static AABB Infinite(); - static AABB Zero(); - - float longest_radius() const; - Vector3 nearestPoint(const Vector3 & v) const; -}; - -} // namespace kraken - -namespace std { - template<> - struct hash { - public: - size_t operator()(const kraken::AABB &s) const - { - size_t h1 = hash()(s.min); - size_t h2 = hash()(s.max); - return h1 ^ ( h2 << 1 ); - } - }; -} // namespace std - - -#endif /* defined(KRAKEN_AABB_H) */ diff --git a/kraken/public/hitinfo.h b/kraken/public/hitinfo.h deleted file mode 100755 index 896355b..0000000 --- a/kraken/public/hitinfo.h +++ /dev/null @@ -1,65 +0,0 @@ -// -// hitinfo.h -// Kraken -// -// Copyright 2018 Kearwood Gilbert. All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, are -// permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of -// conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list -// of conditions and the following disclaimer in the documentation and/or other materials -// provided with the distribution. -// -// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED -// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR -// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// The views and conclusions contained in the software and documentation are those of the -// authors and should not be interpreted as representing official policies, either expressed -// or implied, of Kearwood Gilbert. -// - -#ifndef KRAKEN_HITINFO_H -#define KRAKEN_HITINFO_H - -#include "vector3.h" - -class KRNode; - -namespace kraken { - -class HitInfo { -public: - HitInfo(); - HitInfo(const Vector3 &position, const Vector3 &normal, const float distance); - HitInfo(const Vector3 &position, const Vector3 &normal, const float distance, KRNode *node); - ~HitInfo(); - - Vector3 getPosition() const; - Vector3 getNormal() const; - float getDistance() const; - KRNode *getNode() const; - bool didHit() const; - - HitInfo& operator =(const HitInfo& b); - -private: - KRNode *m_node; - Vector3 m_position; - Vector3 m_normal; - float m_distance; -}; - -} // namespace kraken - -#endif diff --git a/kraken/public/kraken.h b/kraken/public/kraken.h index 5223841..b186755 100644 --- a/kraken/public/kraken.h +++ b/kraken/public/kraken.h @@ -1,44 +1,35 @@ -// -// Kraken -// -// Copyright 2018 Kearwood Gilbert. All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, are -// permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of -// conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list -// of conditions and the following disclaimer in the documentation and/or other materials -// provided with the distribution. -// -// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED -// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR -// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// The views and conclusions contained in the software and documentation are those of the -// authors and should not be interpreted as representing official policies, either expressed -// or implied, of Kearwood Gilbert. -// - -#ifndef KRAKEN_H -#define KRAKEN_H - -#include "scalar.h" -#include "vector2.h" -#include "vector3.h" -#include "vector4.h" -#include "matrix4.h" -#include "quaternion.h" -#include "aabb.h" -#include "triangle3.h" -#include "hitinfo.h" - -#endif // KRAKEN_H +// +// Kraken +// +// Copyright 2018 Kearwood Gilbert. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other materials +// provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// The views and conclusions contained in the software and documentation are those of the +// authors and should not be interpreted as representing official policies, either expressed +// or implied, of Kearwood Gilbert. +// + +#ifndef KRAKEN_H +#define KRAKEN_H + + +#endif // KRAKEN_H diff --git a/kraken/public/matrix4.h b/kraken/public/matrix4.h deleted file mode 100644 index 0ab09bc..0000000 --- a/kraken/public/matrix4.h +++ /dev/null @@ -1,136 +0,0 @@ -// -// Matrix4.h -// Kraken -// -// Copyright 2018 Kearwood Gilbert. All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, are -// permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of -// conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list -// of conditions and the following disclaimer in the documentation and/or other materials -// provided with the distribution. -// -// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED -// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR -// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// The views and conclusions contained in the software and documentation are those of the -// authors and should not be interpreted as representing official policies, either expressed -// or implied, of Kearwood Gilbert. -// - - -#include "vector3.h" -#include "vector4.h" - -#ifndef KRAKEN_MATRIX4_H -#define KRAKEN_MATRIX4_H - -namespace kraken { - -typedef enum { - X_AXIS, - Y_AXIS, - Z_AXIS -} AXIS; - -class Quaternion; - -class Matrix4 { -public: - - union { - struct { - Vector4 axis_x, axis_y, axis_z, transform; - }; - // Matrix components, in column-major order - float c[16]; - }; - - // Default constructor - Creates an identity matrix - Matrix4(); - - Matrix4(float *pMat); - - Matrix4(const Vector3 &new_axis_x, const Vector3 &new_axis_y, const Vector3 &new_axis_z, const Vector3 &new_transform); - - // Destructor - ~Matrix4(); - - // Copy constructor - Matrix4(const Matrix4 &m); - - // Overload assignment operator - Matrix4& operator=(const Matrix4 &m); - - // Overload comparison operator - bool operator==(const Matrix4 &m) const; - - // Overload compound multiply operator - Matrix4& operator*=(const Matrix4 &m); - - float& operator[](unsigned i); - float operator[](unsigned i) const; - - // Overload multiply operator - //Matrix4& operator*(const Matrix4 &m); - Matrix4 operator*(const Matrix4 &m) const; - - float *getPointer(); - - void perspective(float fov, float aspect, float nearz, float farz); - void ortho(float left, float right, float top, float bottom, float nearz, float farz); - void translate(float x, float y, float z); - void translate(const Vector3 &v); - void scale(float x, float y, float z); - void scale(const Vector3 &v); - void scale(float s); - void rotate(float angle, AXIS axis); - void rotate(const Quaternion &q); - void bias(); - bool invert(); - void transpose(); - - static Vector3 DotNoTranslate(const Matrix4 &m, const Vector3 &v); // Dot product without including translation; useful for transforming normals and tangents - static Matrix4 Invert(const Matrix4 &m); - static Matrix4 Transpose(const Matrix4 &m); - static Vector3 Dot(const Matrix4 &m, const Vector3 &v); - static Vector4 Dot4(const Matrix4 &m, const Vector4 &v); - static float DotW(const Matrix4 &m, const Vector3 &v); - static Vector3 DotWDiv(const Matrix4 &m, const Vector3 &v); - - static Matrix4 LookAt(const Vector3 &cameraPos, const Vector3 &lookAtPos, const Vector3 &upDirection); - - static Matrix4 Translation(const Vector3 &v); - static Matrix4 Rotation(const Vector3 &v); - static Matrix4 Scaling(const Vector3 &v); -}; - -} // namespace kraken - -namespace std { - template<> - struct hash { - public: - size_t operator()(const kraken::Matrix4 &s) const - { - size_t h1 = hash()(s.axis_x); - size_t h2 = hash()(s.axis_y); - size_t h3 = hash()(s.axis_z); - size_t h4 = hash()(s.transform); - return h1 ^ (h2 << 1) ^ (h3 << 2) ^ (h4 << 3); - } - }; -} // namespace std - -#endif // KRAKEN_MATRIX4_H diff --git a/kraken/public/quaternion.h b/kraken/public/quaternion.h deleted file mode 100644 index 0ca13db..0000000 --- a/kraken/public/quaternion.h +++ /dev/null @@ -1,110 +0,0 @@ -// -// Quaternion.h -// Kraken -// -// Copyright 2018 Kearwood Gilbert. All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, are -// permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of -// conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list -// of conditions and the following disclaimer in the documentation and/or other materials -// provided with the distribution. -// -// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED -// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR -// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// The views and conclusions contained in the software and documentation are those of the -// authors and should not be interpreted as representing official policies, either expressed -// or implied, of Kearwood Gilbert. -// - -#ifndef KRAKEN_QUATERNION_H -#define KRAKEN_QUATERNION_H - -#include "vector3.h" - -namespace kraken { - -class Quaternion { -public: - union { - struct { - float w, x, y, z; - }; - float c[4]; - }; - - Quaternion(); - Quaternion(float w, float x, float y, float z); - Quaternion(const Quaternion& p); - Quaternion(const Vector3 &euler); - Quaternion(const Vector3 &from_vector, const Vector3 &to_vector); - ~Quaternion(); - - Quaternion& operator =( const Quaternion& p ); - Quaternion operator +(const Quaternion &v) const; - Quaternion operator -(const Quaternion &v) const; - Quaternion operator +() const; - Quaternion operator -() const; - - Quaternion operator *(const Quaternion &v); - Quaternion operator *(float num) const; - Quaternion operator /(float num) const; - - Quaternion& operator +=(const Quaternion& v); - Quaternion& operator -=(const Quaternion& v); - Quaternion& operator *=(const Quaternion& v); - Quaternion& operator *=(const float& v); - Quaternion& operator /=(const float& v); - - friend bool operator ==(Quaternion &v1, Quaternion &v2); - friend bool operator !=(Quaternion &v1, Quaternion &v2); - float& operator [](unsigned i); - float operator [](unsigned i) const; - - void setEulerXYZ(const Vector3 &euler); - void setEulerZYX(const Vector3 &euler); - Vector3 eulerXYZ() const; - Matrix4 rotationMatrix() const; - - void normalize(); - static Quaternion Normalize(const Quaternion &v1); - - void conjugate(); - static Quaternion Conjugate(const Quaternion &v1); - - static Quaternion FromAngleAxis(const Vector3 &axis, float angle); - static Quaternion Lerp(const Quaternion &a, const Quaternion &b, float t); - static Quaternion Slerp(const Quaternion &a, const Quaternion &b, float t); - static float Dot(const Quaternion &v1, const Quaternion &v2); -}; - -} // namespace kraken - -namespace std { - template<> - struct hash { - public: - size_t operator()(const kraken::Quaternion &s) const - { - size_t h1 = hash()(s.c[0]); - size_t h2 = hash()(s.c[1]); - size_t h3 = hash()(s.c[2]); - size_t h4 = hash()(s.c[3]); - return h1 ^ (h2 << 1) ^ (h3 << 2) ^ (h4 << 3); - } - }; -} // namespace std - -#endif // KRAKEN_QUATERNION_H diff --git a/kraken/public/scalar.h b/kraken/public/scalar.h deleted file mode 100644 index 5dd0423..0000000 --- a/kraken/public/scalar.h +++ /dev/null @@ -1,41 +0,0 @@ -// -// KRFloat.h -// Kraken -// -// Copyright 2018 Kearwood Gilbert. All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, are -// permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of -// conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list -// of conditions and the following disclaimer in the documentation and/or other materials -// provided with the distribution. -// -// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED -// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR -// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// The views and conclusions contained in the software and documentation are those of the -// authors and should not be interpreted as representing official policies, either expressed -// or implied, of Kearwood Gilbert. -// - -#ifndef KRAKEN_SCALAR_H -#define KRAKEN_SCALAR_H - -namespace kraken { - - float SmoothStep(float a, float b, float t); - -}; // namespace kraken - -#endif // KRAKEN_SCALAR_H diff --git a/kraken/public/triangle3.h b/kraken/public/triangle3.h deleted file mode 100644 index 6c5ce29..0000000 --- a/kraken/public/triangle3.h +++ /dev/null @@ -1,79 +0,0 @@ -// -// KRTriangle.h -// Kraken -// -// Copyright 2018 Kearwood Gilbert. All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, are -// permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of -// conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list -// of conditions and the following disclaimer in the documentation and/or other materials -// provided with the distribution. -// -// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED -// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR -// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// The views and conclusions contained in the software and documentation are those of the -// authors and should not be interpreted as representing official policies, either expressed -// or implied, of Kearwood Gilbert. -// - -#ifndef KRAKEN_TRIANGLE3_H -#define KRAKEN_TRIANGLE3_H - -#include "vector3.h" - -namespace kraken { - -class Triangle3 -{ -public: - Vector3 vert[3]; - - Triangle3(const Triangle3 &tri); - Triangle3(const Vector3 &v1, const Vector3 &v2, const Vector3 &v3); - ~Triangle3(); - - Vector3 calculateNormal() const; - - bool operator ==(const Triangle3& b) const; - bool operator !=(const Triangle3& b) const; - Triangle3& operator =(const Triangle3& b); - Vector3& operator[](unsigned int i); - Vector3 operator[](unsigned int i) const; - - bool rayCast(const Vector3 &start, const Vector3 &dir, Vector3 &hit_point) const; - bool sphereCast(const Vector3 &start, const Vector3 &dir, float radius, Vector3 &hit_point, float &hit_distance) const; - - bool containsPoint(const Vector3 &p) const; - Vector3 closestPointOnTriangle(const Vector3 &p) const; -}; - -} // namespace kraken - -namespace std { - template<> - struct hash { - public: - size_t operator()(const kraken::Triangle3 &s) const - { - size_t h1 = hash()(s.vert[0]); - size_t h2 = hash()(s.vert[1]); - size_t h3 = hash()(s.vert[2]); - return h1 ^ (h2 << 1) ^ (h3 << 2); - } - }; -} // namespace std - -#endif // KRAKEN_TRIANGLE3_H diff --git a/kraken/public/vector2.h b/kraken/public/vector2.h deleted file mode 100644 index a7af550..0000000 --- a/kraken/public/vector2.h +++ /dev/null @@ -1,117 +0,0 @@ -// -// vector2.h -// Kraken -// -// Copyright 2018 Kearwood Gilbert. All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, are -// permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of -// conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list -// of conditions and the following disclaimer in the documentation and/or other materials -// provided with the distribution. -// -// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED -// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR -// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// The views and conclusions contained in the software and documentation are those of the -// authors and should not be interpreted as representing official policies, either expressed -// or implied, of Kearwood Gilbert. -// - -#ifndef KRAKEN_VECTOR2_H -#define KRAKEN_VECTOR2_H - -#include // for hash<> -#include // for std::numeric_limits<> -#include // for sqrtf - -namespace kraken { - -class Vector2 { - -public: - union { - struct { - float x, y; - }; - float c[2]; - }; - - Vector2(); - Vector2(float X, float Y); - Vector2(float v); - Vector2(float *v); - Vector2(const Vector2 &v); - ~Vector2(); - - // Vector2 swizzle getters - Vector2 yx() const; - - // Vector2 swizzle setters - void yx(const Vector2 &v); - - Vector2& operator =(const Vector2& b); - Vector2 operator +(const Vector2& b) const; - Vector2 operator -(const Vector2& b) const; - Vector2 operator +() const; - Vector2 operator -() const; - Vector2 operator *(const float v) const; - Vector2 operator /(const float v) const; - - Vector2& operator +=(const Vector2& b); - Vector2& operator -=(const Vector2& b); - Vector2& operator *=(const float v); - Vector2& operator /=(const float v); - - // Comparison operators are implemented to allow insertion into sorted containers such as std::set - bool operator >(const Vector2& b) const; - bool operator <(const Vector2& b) const; - - bool operator ==(const Vector2& b) const; - bool operator !=(const Vector2& b) const; - - float& operator[](unsigned i); - float operator[](unsigned i) const; - - float sqrMagnitude() const; - float magnitude() const; - - void normalize(); - static Vector2 Normalize(const Vector2 &v); - - static float Cross(const Vector2 &v1, const Vector2 &v2); - - static float Dot(const Vector2 &v1, const Vector2 &v2); - static Vector2 Min(); - static Vector2 Max(); - static Vector2 Zero(); - static Vector2 One(); -}; - -} // namespace kraken - -namespace std { - template<> - struct hash { - public: - size_t operator()(const kraken::Vector2 &s) const - { - size_t h1 = hash()(s.x); - size_t h2 = hash()(s.y); - return h1 ^ (h2 << 1); - } - }; -} // namespace std - -#endif // KRAKEN_VECTOR2_H diff --git a/kraken/public/vector3.h b/kraken/public/vector3.h deleted file mode 100644 index e9f6551..0000000 --- a/kraken/public/vector3.h +++ /dev/null @@ -1,146 +0,0 @@ -// -// Vector3.h -// Kraken -// -// Copyright 2018 Kearwood Gilbert. All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, are -// permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of -// conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list -// of conditions and the following disclaimer in the documentation and/or other materials -// provided with the distribution. -// -// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED -// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR -// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// The views and conclusions contained in the software and documentation are those of the -// authors and should not be interpreted as representing official policies, either expressed -// or implied, of Kearwood Gilbert. -// - -#ifndef KRAKEN_VECTOR3_H -#define KRAKEN_VECTOR3_H - -#include // for hash<> - -#include "vector2.h" -#include "vector4.h" - -namespace kraken { - -class Vector3 { - -public: - union { - struct { - float x, y, z; - }; - float c[3]; - }; - - Vector3(); - Vector3(float X, float Y, float Z); - Vector3(float v); - Vector3(float *v); - Vector3(double *v); - Vector3(const Vector3 &v); - Vector3(const Vector4 &v); - ~Vector3(); - - // Vector2 swizzle getters - Vector2 xx() const; - Vector2 xy() const; - Vector2 xz() const; - Vector2 yx() const; - Vector2 yy() const; - Vector2 yz() const; - Vector2 zx() const; - Vector2 zy() const; - Vector2 zz() const; - - // Vector2 swizzle setters - void xy(const Vector2 &v); - void xz(const Vector2 &v); - void yx(const Vector2 &v); - void yz(const Vector2 &v); - void zx(const Vector2 &v); - void zy(const Vector2 &v); - - Vector3& operator =(const Vector3& b); - Vector3& operator =(const Vector4& b); - Vector3 operator +(const Vector3& b) const; - Vector3 operator -(const Vector3& b) const; - Vector3 operator +() const; - Vector3 operator -() const; - Vector3 operator *(const float v) const; - Vector3 operator /(const float v) const; - - Vector3& operator +=(const Vector3& b); - Vector3& operator -=(const Vector3& b); - Vector3& operator *=(const float v); - Vector3& operator /=(const float v); - - bool operator ==(const Vector3& b) const; - bool operator !=(const Vector3& b) const; - - // Comparison operators are implemented to allow insertion into sorted containers such as std::set - bool operator >(const Vector3& b) const; - bool operator <(const Vector3& b) const; - - float& operator[](unsigned i); - float operator[](unsigned i) const; - - float sqrMagnitude() const; // calculate the square of the magnitude (useful for comparison of magnitudes without the cost of a sqrt() function) - float magnitude() const; - - void scale(const Vector3 &v); - void normalize(); - static Vector3 Normalize(const Vector3 &v); - - static Vector3 Cross(const Vector3 &v1, const Vector3 &v2); - - static float Dot(const Vector3 &v1, const Vector3 &v2); - static Vector3 Min(); - static Vector3 Max(); - static const Vector3 &Zero(); - static Vector3 One(); - static Vector3 Forward(); - static Vector3 Backward(); - static Vector3 Up(); - static Vector3 Down(); - static Vector3 Left(); - static Vector3 Right(); - static Vector3 Scale(const Vector3 &v1, const Vector3 &v2); - static Vector3 Lerp(const Vector3 &v1, const Vector3 &v2, float d); - static Vector3 Slerp(const Vector3 &v1, const Vector3 &v2, float d); - static void OrthoNormalize(Vector3 &normal, Vector3 &tangent); // Gram-Schmidt Orthonormalization -}; - -} // namespace kraken - -namespace std { - template<> - struct hash { - public: - size_t operator()(const kraken::Vector3 &s) const - { - size_t h1 = hash()(s.x); - size_t h2 = hash()(s.y); - size_t h3 = hash()(s.z); - return h1 ^ (h2 << 1) ^ (h3 << 2); - } - }; -} // namespace std - -#endif // KRAKEN_VECTOR3_H diff --git a/kraken/public/vector4.h b/kraken/public/vector4.h deleted file mode 100644 index 53056f1..0000000 --- a/kraken/public/vector4.h +++ /dev/null @@ -1,122 +0,0 @@ -// -// Vector4.h -// Kraken -// -// Copyright 2018 Kearwood Gilbert. All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, are -// permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of -// conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list -// of conditions and the following disclaimer in the documentation and/or other materials -// provided with the distribution. -// -// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED -// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR -// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// The views and conclusions contained in the software and documentation are those of the -// authors and should not be interpreted as representing official policies, either expressed -// or implied, of Kearwood Gilbert. -// - -#ifndef KRAKEN_VECTOR4_H -#define KRAKEN_VECTOR4_H - -#include // for hash<> - -namespace kraken { - -class Vector3; - -class Vector4 { - -public: - union { - struct { - float x, y, z, w; - }; - float c[4]; - }; - - Vector4(); - Vector4(float X, float Y, float Z, float W); - Vector4(float v); - Vector4(float *v); - Vector4(const Vector4 &v); - Vector4(const Vector3 &v, float W); - ~Vector4(); - - - Vector4& operator =(const Vector4& b); - Vector4 operator +(const Vector4& b) const; - Vector4 operator -(const Vector4& b) const; - Vector4 operator +() const; - Vector4 operator -() const; - Vector4 operator *(const float v) const; - Vector4 operator /(const float v) const; - - Vector4& operator +=(const Vector4& b); - Vector4& operator -=(const Vector4& b); - Vector4& operator *=(const float v); - Vector4& operator /=(const float v); - - bool operator ==(const Vector4& b) const; - bool operator !=(const Vector4& b) const; - - // Comparison operators are implemented to allow insertion into sorted containers such as std::set - bool operator >(const Vector4& b) const; - bool operator <(const Vector4& b) const; - - float& operator[](unsigned i); - float operator[](unsigned i) const; - - float sqrMagnitude() const; // calculate the square of the magnitude (useful for comparison of magnitudes without the cost of a sqrt() function) - float magnitude() const; - - void normalize(); - static Vector4 Normalize(const Vector4 &v); - - static float Dot(const Vector4 &v1, const Vector4 &v2); - static Vector4 Min(); - static Vector4 Max(); - static const Vector4 &Zero(); - static Vector4 One(); - static Vector4 Forward(); - static Vector4 Backward(); - static Vector4 Up(); - static Vector4 Down(); - static Vector4 Left(); - static Vector4 Right(); - static Vector4 Lerp(const Vector4 &v1, const Vector4 &v2, float d); - static Vector4 Slerp(const Vector4 &v1, const Vector4 &v2, float d); - static void OrthoNormalize(Vector4 &normal, Vector4 &tangent); // Gram-Schmidt Orthonormalization -}; - -} // namespace kraken - -namespace std { - template<> - struct hash { - public: - size_t operator()(const kraken::Vector4 &s) const - { - size_t h1 = hash()(s.x); - size_t h2 = hash()(s.y); - size_t h3 = hash()(s.z); - size_t h4 = hash()(s.w); - return h1 ^ (h2 << 1) ^ (h3 << 2) ^ (h4 << 3); - } - }; -} // namespace std - -#endif // KRAKEN_VECTOR4_H diff --git a/kraken/quaternion.cpp b/kraken/quaternion.cpp deleted file mode 100644 index 794c874..0000000 --- a/kraken/quaternion.cpp +++ /dev/null @@ -1,365 +0,0 @@ -// -// Quaternion.cpp -// KREngine -// -// Copyright 2012 Kearwood Gilbert. All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, are -// permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of -// conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list -// of conditions and the following disclaimer in the documentation and/or other materials -// provided with the distribution. -// -// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED -// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR -// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// The views and conclusions contained in the software and documentation are those of the -// authors and should not be interpreted as representing official policies, either expressed -// or implied, of Kearwood Gilbert. -// - -#include "public/kraken.h" - -#include "KRHelpers.h" - -namespace kraken { - -Quaternion::Quaternion() { - c[0] = 1.0; - c[1] = 0.0; - c[2] = 0.0; - c[3] = 0.0; -} - -Quaternion::Quaternion(float w, float x, float y, float z) { - c[0] = w; - c[1] = x; - c[2] = y; - c[3] = z; -} - -Quaternion::Quaternion(const Quaternion& p) { - c[0] = p[0]; - c[1] = p[1]; - c[2] = p[2]; - c[3] = p[3]; -} - -Quaternion& Quaternion::operator =( const Quaternion& p ) { - c[0] = p[0]; - c[1] = p[1]; - c[2] = p[2]; - c[3] = p[3]; - return *this; -} - -Quaternion::Quaternion(const Vector3 &euler) { - setEulerZYX(euler); -} - -Quaternion::Quaternion(const Vector3 &from_vector, const Vector3 &to_vector) { - - Vector3 a = Vector3::Cross(from_vector, to_vector); - c[0] = a[0]; - c[1] = a[1]; - c[2] = a[2]; - c[3] = sqrt(from_vector.sqrMagnitude() * to_vector.sqrMagnitude()) + Vector3::Dot(from_vector, to_vector); - normalize(); -} - -Quaternion::~Quaternion() { - -} - -void Quaternion::setEulerXYZ(const Vector3 &euler) -{ - *this = Quaternion::FromAngleAxis(Vector3(1.0f, 0.0f, 0.0f), euler.x) - * Quaternion::FromAngleAxis(Vector3(0.0f, 1.0f, 0.0f), euler.y) - * Quaternion::FromAngleAxis(Vector3(0.0f, 0.0f, 1.0f), euler.z); -} - -void Quaternion::setEulerZYX(const Vector3 &euler) { - // ZYX Order! - float c1 = cos(euler[0] * 0.5f); - float c2 = cos(euler[1] * 0.5f); - float c3 = cos(euler[2] * 0.5f); - float s1 = sin(euler[0] * 0.5f); - float s2 = sin(euler[1] * 0.5f); - float s3 = sin(euler[2] * 0.5f); - - c[0] = c1 * c2 * c3 + s1 * s2 * s3; - c[1] = s1 * c2 * c3 - c1 * s2 * s3; - c[2] = c1 * s2 * c3 + s1 * c2 * s3; - c[3] = c1 * c2 * s3 - s1 * s2 * c3; -} - -float Quaternion::operator [](unsigned i) const { - return c[i]; -} - -float &Quaternion::operator [](unsigned i) { - return c[i]; -} - -Vector3 Quaternion::eulerXYZ() const { - double a2 = 2 * (c[0] * c[2] - c[1] * c[3]); - if(a2 <= -0.99999) { - return Vector3( - 2.0 * atan2(c[1], c[0]), - -PI * 0.5f, - 0 - ); - } else if(a2 >= 0.99999) { - return Vector3( - 2.0 * atan2(c[1], c[0]), - PI * 0.5f, - 0 - ); - } else { - return Vector3( - atan2(2 * (c[0] * c[1] + c[2] * c[3]), (1 - 2 * (c[1] * c[1] + c[2] * c[2]))), - asin(a2), - atan2(2 * (c[0] * c[3] + c[1] * c[2]), (1 - 2 * (c[2] * c[2] + c[3] * c[3]))) - ); - } - - -} - -bool operator ==(Quaternion &v1, Quaternion &v2) { - return - v1[0] == v2[0] - && v1[1] == v2[1] - && v1[2] == v2[2] - && v1[3] == v2[3]; -} - -bool operator !=(Quaternion &v1, Quaternion &v2) { - return - v1[0] != v2[0] - || v1[1] != v2[1] - || v1[2] != v2[2] - || v1[3] != v2[3]; -} - -Quaternion Quaternion::operator *(const Quaternion &v) { - float t0 = (c[3]-c[2])*(v[2]-v[3]); - float t1 = (c[0]+c[1])*(v[0]+v[1]); - float t2 = (c[0]-c[1])*(v[2]+v[3]); - float t3 = (c[3]+c[2])*(v[0]-v[1]); - float t4 = (c[3]-c[1])*(v[1]-v[2]); - float t5 = (c[3]+c[1])*(v[1]+v[2]); - float t6 = (c[0]+c[2])*(v[0]-v[3]); - float t7 = (c[0]-c[2])*(v[0]+v[3]); - float t8 = t5+t6+t7; - float t9 = (t4+t8)/2; - - return Quaternion( - t0+t9-t5, - t1+t9-t8, - t2+t9-t7, - t3+t9-t6 - ); -} - -Quaternion Quaternion::operator *(float v) const { - return Quaternion(c[0] * v, c[1] * v, c[2] * v, c[3] * v); -} - -Quaternion Quaternion::operator /(float num) const { - float inv_num = 1.0f / num; - return Quaternion(c[0] * inv_num, c[1] * inv_num, c[2] * inv_num, c[3] * inv_num); -} - -Quaternion Quaternion::operator +(const Quaternion &v) const { - return Quaternion(c[0] + v[0], c[1] + v[1], c[2] + v[2], c[3] + v[3]); -} - -Quaternion Quaternion::operator -(const Quaternion &v) const { - return Quaternion(c[0] - v[0], c[1] - v[1], c[2] - v[2], c[3] - v[3]); -} - -Quaternion& Quaternion::operator +=(const Quaternion& v) { - c[0] += v[0]; - c[1] += v[1]; - c[2] += v[2]; - c[3] += v[3]; - return *this; -} - -Quaternion& Quaternion::operator -=(const Quaternion& v) { - c[0] -= v[0]; - c[1] -= v[1]; - c[2] -= v[2]; - c[3] -= v[3]; - return *this; -} - -Quaternion& Quaternion::operator *=(const Quaternion& v) { - float t0 = (c[3]-c[2])*(v[2]-v[3]); - float t1 = (c[0]+c[1])*(v[0]+v[1]); - float t2 = (c[0]-c[1])*(v[2]+v[3]); - float t3 = (c[3]+c[2])*(v[0]-v[1]); - float t4 = (c[3]-c[1])*(v[1]-v[2]); - float t5 = (c[3]+c[1])*(v[1]+v[2]); - float t6 = (c[0]+c[2])*(v[0]-v[3]); - float t7 = (c[0]-c[2])*(v[0]+v[3]); - float t8 = t5+t6+t7; - float t9 = (t4+t8)/2; - - c[0] = t0+t9-t5; - c[1] = t1+t9-t8; - c[2] = t2+t9-t7; - c[3] = t3+t9-t6; - - return *this; -} - -Quaternion& Quaternion::operator *=(const float& v) { - c[0] *= v; - c[1] *= v; - c[2] *= v; - c[3] *= v; - return *this; -} - -Quaternion& Quaternion::operator /=(const float& v) { - float inv_v = 1.0f / v; - c[0] *= inv_v; - c[1] *= inv_v; - c[2] *= inv_v; - c[3] *= inv_v; - return *this; -} - -Quaternion Quaternion::operator +() const { - return *this; -} - -Quaternion Quaternion::operator -() const { - return Quaternion(-c[0], -c[1], -c[2], -c[3]); -} - -Quaternion Normalize(const Quaternion &v1) { - float inv_magnitude = 1.0f / sqrtf(v1[0] * v1[0] + v1[1] * v1[1] + v1[2] * v1[2] + v1[3] * v1[3]); - return Quaternion( - v1[0] * inv_magnitude, - v1[1] * inv_magnitude, - v1[2] * inv_magnitude, - v1[3] * inv_magnitude - ); -} - -void Quaternion::normalize() { - float inv_magnitude = 1.0f / sqrtf(c[0] * c[0] + c[1] * c[1] + c[2] * c[2] + c[3] * c[3]); - c[0] *= inv_magnitude; - c[1] *= inv_magnitude; - c[2] *= inv_magnitude; - c[3] *= inv_magnitude; -} - -Quaternion Conjugate(const Quaternion &v1) { - return Quaternion(v1[0], -v1[1], -v1[2], -v1[3]); -} - -void Quaternion::conjugate() { - c[1] = -c[1]; - c[2] = -c[2]; - c[3] = -c[3]; -} - -Matrix4 Quaternion::rotationMatrix() const { - Matrix4 matRotate; - - /* - Vector3 euler = eulerXYZ(); - - matRotate.rotate(euler.x, X_AXIS); - matRotate.rotate(euler.y, Y_AXIS); - matRotate.rotate(euler.z, Z_AXIS); - */ - - // FINDME - Determine why the more optimal routine commented below wasn't working - - - matRotate.c[0] = 1.0 - 2.0 * (c[2] * c[2] + c[3] * c[3]); - matRotate.c[1] = 2.0 * (c[1] * c[2] - c[0] * c[3]); - matRotate.c[2] = 2.0 * (c[0] * c[2] + c[1] * c[3]); - - matRotate.c[4] = 2.0 * (c[1] * c[2] + c[0] * c[3]); - matRotate.c[5] = 1.0 - 2.0 * (c[1] * c[1] + c[3] * c[3]); - matRotate.c[6] = 2.0 * (c[2] * c[3] - c[0] * c[1]); - - matRotate.c[8] = 2.0 * (c[1] * c[3] - c[0] * c[2]); - matRotate.c[9] = 2.0 * (c[0] * c[1] + c[2] * c[3]); - matRotate.c[10] = 1.0 - 2.0 * (c[1] * c[1] + c[2] * c[2]); - - return matRotate; -} - - -Quaternion Quaternion::FromAngleAxis(const Vector3 &axis, float angle) -{ - float ha = angle * 0.5f; - float sha = sin(ha); - return Quaternion(cos(ha), axis.x * sha, axis.y * sha, axis.z * sha); -} - -float Quaternion::Dot(const Quaternion &v1, const Quaternion &v2) -{ - return v1.c[0] * v2.c[0] + v1.c[1] * v2.c[1] + v1.c[2] * v2.c[2] + v1.c[3] * v2.c[3]; -} - -Quaternion Quaternion::Lerp(const Quaternion &a, const Quaternion &b, float t) -{ - if (t <= 0.0f) { - return a; - } else if (t >= 1.0f) { - return b; - } - - return a * (1.0f - t) + b * t; -} - -Quaternion Quaternion::Slerp(const Quaternion &a, const Quaternion &b, float t) -{ - if (t <= 0.0f) { - return a; - } - - if (t >= 1.0f) { - return b; - } - - float coshalftheta = Dot(a, b); - Quaternion c = a; - - // Angle is greater than 180. We can negate the angle/quat to get the - // shorter rotation to reach the same destination. - if ( coshalftheta < 0.0f ) { - coshalftheta = -coshalftheta; - c = -c; - } - - if ( coshalftheta > (1.0f - std::numeric_limits::epsilon())) { - // Angle is tiny - save some computation by lerping instead. - return Lerp(c, b, t); - } - - float halftheta = acos(coshalftheta); - - return (c * sin((1.0f - t) * halftheta) + b * sin(t * halftheta)) / sin(halftheta); -} - -} // namespace kraken diff --git a/kraken/scalar.cpp b/kraken/scalar.cpp deleted file mode 100755 index 8b7a180..0000000 --- a/kraken/scalar.cpp +++ /dev/null @@ -1,19 +0,0 @@ -// -// KRFloat.cpp -// Kraken -// -// Created by Kearwood Gilbert on 2013-05-03. -// Copyright (c) 2013 Kearwood Software. All rights reserved. -// - -#include "public/kraken.h" - -namespace kraken { - -float SmoothStep(float a, float b, float t) -{ - float d = (3.0 * t * t - 2.0 * t * t * t); - return a * (1.0f - d) + b * d; -} - -} // namespace kraken diff --git a/kraken/triangle3.cpp b/kraken/triangle3.cpp deleted file mode 100644 index 93ccfc1..0000000 --- a/kraken/triangle3.cpp +++ /dev/null @@ -1,331 +0,0 @@ -// -// KRTriangle.cpp -// Kraken -// -// Created by Kearwood Gilbert on 2/8/2014. -// Copyright (c) 2014 Kearwood Software. All rights reserved. -// - -#include "public/kraken.h" - -using namespace kraken; - -namespace { - bool _intersectSphere(const Vector3 &start, const Vector3 &dir, const Vector3 &sphere_center, float sphere_radius, float &distance) - { - // dir must be normalized - - // From: http://archive.gamedev.net/archive/reference/articles/article1026.html - - // TODO - Move to another class? - Vector3 Q = sphere_center - start; - float c = Q.magnitude(); - float v = Vector3::Dot(Q, dir); - float d = sphere_radius * sphere_radius - (c * c - v * v); - - - - if (d < 0.0) { - // No intersection - return false; - } - - // Return the distance to the [first] intersecting point - - distance = v - sqrt(d); - if (distance < 0.0f) { - return false; - } - return true; - - } - - bool _sameSide(const Vector3 &p1, const Vector3 &p2, const Vector3 &a, const Vector3 &b) - { - // TODO - Move to Vector3 class? - // From: http://stackoverflow.com/questions/995445/determine-if-a-3d-point-is-within-a-triangle - - Vector3 cp1 = Vector3::Cross(b - a, p1 - a); - Vector3 cp2 = Vector3::Cross(b - a, p2 - a); - if (Vector3::Dot(cp1, cp2) >= 0) return true; - return false; - } - - Vector3 _closestPointOnLine(const Vector3 &a, const Vector3 &b, const Vector3 &p) - { - // From: http://stackoverflow.com/questions/995445/determine-if-a-3d-point-is-within-a-triangle - - // Determine t (the length of the vector from ‘a’ to ‘p’) - - Vector3 c = p - a; - Vector3 V = Vector3::Normalize(b - a); - float d = (a - b).magnitude(); - float t = Vector3::Dot(V, c); - - // Check to see if ‘t’ is beyond the extents of the line segment - - if (t < 0) return a; - if (t > d) return b; - - // Return the point between ‘a’ and ‘b’ - - return a + V * t; - } -} // anonymous namespace - -namespace kraken { - -Triangle3::Triangle3(const Vector3 &v1, const Vector3 &v2, const Vector3 &v3) -{ - vert[0] = v1; - vert[1] = v2; - vert[2] = v3; -} - -Triangle3::Triangle3(const Triangle3 &tri) -{ - vert[0] = tri[0]; - vert[1] = tri[1]; - vert[3] = tri[3]; -} - - -Triangle3::~Triangle3() -{ - -} - -bool Triangle3::operator ==(const Triangle3& b) const -{ - return vert[0] == b[0] && vert[1] == b[1] && vert[2] == b[2]; -} - -bool Triangle3::operator !=(const Triangle3& b) const -{ - return vert[0] != b[0] || vert[1] != b[1] || vert[2] != b[2]; -} - -Triangle3& Triangle3::operator =(const Triangle3& b) -{ - - vert[0] = b[0]; - vert[1] = b[1]; - vert[3] = b[3]; - return *this; -} - -Vector3& Triangle3::operator[](unsigned int i) -{ - return vert[i]; -} - -Vector3 Triangle3::operator[](unsigned int i) const -{ - return vert[i]; -} - - -bool Triangle3::rayCast(const Vector3 &start, const Vector3 &dir, Vector3 &hit_point) const -{ - // algorithm based on Dan Sunday's implementation at http://geomalgorithms.com/a06-_intersect-2.html - const float SMALL_NUM = 0.00000001; // anything that avoids division overflow - Vector3 u, v, n; // triangle vectors - Vector3 w0, w; // ray vectors - float r, a, b; // params to calc ray-plane intersect - - // get triangle edge vectors and plane normal - u = vert[1] - vert[0]; - v = vert[2] - vert[0]; - n = Vector3::Cross(u, v); // cross product - if (n == Vector3::Zero()) // triangle is degenerate - return false; // do not deal with this case - - w0 = start - vert[0]; - a = -Vector3::Dot(n, w0); - b = Vector3::Dot(n,dir); - if (fabs(b) < SMALL_NUM) { // ray is parallel to triangle plane - if (a == 0) - return false; // ray lies in triangle plane - else { - return false; // ray disjoint from plane - } - } - - // get intersect point of ray with triangle plane - r = a / b; - if (r < 0.0) // ray goes away from triangle - return false; // => no intersect - // for a segment, also test if (r > 1.0) => no intersect - - - Vector3 plane_hit_point = start + dir * r; // intersect point of ray and plane - - // is plane_hit_point inside triangle? - float uu, uv, vv, wu, wv, D; - uu = Vector3::Dot(u,u); - uv = Vector3::Dot(u,v); - vv = Vector3::Dot(v,v); - w = plane_hit_point - vert[0]; - wu = Vector3::Dot(w,u); - wv = Vector3::Dot(w,v); - D = uv * uv - uu * vv; - - // get and test parametric coords - float s, t; - s = (uv * wv - vv * wu) / D; - if (s < 0.0 || s > 1.0) // plane_hit_point is outside triangle - return false; - t = (uv * wu - uu * wv) / D; - if (t < 0.0 || (s + t) > 1.0) // plane_hit_point is outside triangle - return false; - - // plane_hit_point is inside the triangle - hit_point = plane_hit_point; - return true; -} - -Vector3 Triangle3::calculateNormal() const -{ - Vector3 v1 = vert[1] - vert[0]; - Vector3 v2 = vert[2] - vert[0]; - - return Vector3::Normalize(Vector3::Cross(v1, v2)); -} - -Vector3 Triangle3::closestPointOnTriangle(const Vector3 &p) const -{ - Vector3 a = vert[0]; - Vector3 b = vert[1]; - Vector3 c = vert[2]; - - Vector3 Rab = _closestPointOnLine(a, b, p); - Vector3 Rbc = _closestPointOnLine(b, c, p); - Vector3 Rca = _closestPointOnLine(c, a, p); - - // return closest [Rab, Rbc, Rca] to p; - float sd_Rab = (p - Rab).sqrMagnitude(); - float sd_Rbc = (p - Rbc).sqrMagnitude(); - float sd_Rca = (p - Rca).sqrMagnitude(); - - if(sd_Rab < sd_Rbc && sd_Rab < sd_Rca) { - return Rab; - } else if(sd_Rbc < sd_Rab && sd_Rbc < sd_Rca) { - return Rbc; - } else { - return Rca; - } -} - -bool Triangle3::sphereCast(const Vector3 &start, const Vector3 &dir, float radius, Vector3 &hit_point, float &hit_distance) const -{ - // Dir must be normalized - const float SMALL_NUM = 0.001f; // anything that avoids division overflow - - Vector3 tri_normal = calculateNormal(); - - float d = Vector3::Dot(tri_normal, vert[0]); - float e = Vector3::Dot(tri_normal, start) - radius; - float cotangent_distance = e - d; - - Vector3 plane_intersect; - float plane_intersect_distance; - - float denom = Vector3::Dot(tri_normal, dir); - - if(denom > -SMALL_NUM) { - return false; // dir is co-planar with the triangle or going in the direction of the normal; no intersection - } - - // Detect an embedded plane, caused by a sphere that is already intersecting the plane. - if(cotangent_distance <= 0 && cotangent_distance >= -radius * 2.0f) { - // Embedded plane - Sphere is already intersecting the plane. - // Use the point closest to the origin of the sphere as the intersection - plane_intersect = start - tri_normal * (cotangent_distance + radius); - plane_intersect_distance = 0.0f; - } else { - // Sphere is not intersecting the plane - // Determine the first point hit by the swept sphere on the triangle's plane - - plane_intersect_distance = -(cotangent_distance / denom); - plane_intersect = start + dir * plane_intersect_distance - tri_normal * radius; - } - - if(plane_intersect_distance < 0.0f) { - return false; - } - - if(containsPoint(plane_intersect)) { - // Triangle contains point - hit_point = plane_intersect; - hit_distance = plane_intersect_distance; - return true; - } else { - // Triangle does not contain point, cast ray back to sphere from closest point on triangle edge or vertice - Vector3 closest_point = closestPointOnTriangle(plane_intersect); - float reverse_hit_distance; - if(_intersectSphere(closest_point, -dir, start, radius, reverse_hit_distance)) { - // Reverse cast hit sphere - hit_distance = reverse_hit_distance; - hit_point = closest_point; - return true; - } else { - // Reverse cast did not hit sphere - return false; - } - } -} - - -bool Triangle3::containsPoint(const Vector3 &p) const -{ - /* - // From: http://stackoverflow.com/questions/995445/determine-if-a-3d-point-is-within-a-triangle - - const float SMALL_NUM = 0.00000001f; // anything that avoids division overflow - // Vector3 A = vert[0], B = vert[1], C = vert[2]; - if (_sameSide(p, vert[0], vert[1], vert[2]) && _sameSide(p, vert[1], vert[0], vert[2]) && _sameSide(p, vert[2], vert[0], vert[1])) { - Vector3 vc1 = Vector3::Cross(vert[0] - vert[1], vert[0] - vert[2]); - if(fabs(Vector3::Dot(vert[0] - p, vc1)) <= SMALL_NUM) { - return true; - } - } - - return false; - */ - - // From: http://blogs.msdn.com/b/rezanour/archive/2011/08/07/barycentric-coordinates-and-point-in-triangle-tests.aspx - - Vector3 A = vert[0]; - Vector3 B = vert[1]; - Vector3 C = vert[2]; - Vector3 P = p; - - // Prepare our barycentric variables - Vector3 u = B - A; - Vector3 v = C - A; - Vector3 w = P - A; - - Vector3 vCrossW = Vector3::Cross(v, w); - Vector3 vCrossU = Vector3::Cross(v, u); - - // Test sign of r - if (Vector3::Dot(vCrossW, vCrossU) < 0) - return false; - - Vector3 uCrossW = Vector3::Cross(u, w); - Vector3 uCrossV = Vector3::Cross(u, v); - - // Test sign of t - if (Vector3::Dot(uCrossW, uCrossV) < 0) - return false; - - // At this point, we know that r and t and both > 0. - // Therefore, as long as their sum is <= 1, each must be less <= 1 - float denom = uCrossV.magnitude(); - float r = vCrossW.magnitude() / denom; - float t = uCrossW.magnitude() / denom; - - return (r + t <= 1); -} - -} // namespace kraken diff --git a/kraken/vector2.cpp b/kraken/vector2.cpp deleted file mode 100644 index 7bd1cfd..0000000 --- a/kraken/vector2.cpp +++ /dev/null @@ -1,215 +0,0 @@ -// -// Vector2.cpp -// KREngine -// -// Created by Kearwood Gilbert on 12-03-22. -// Copyright (c) 2012 Kearwood Software. All rights reserved. -// - -#include "public/kraken.h" - -namespace kraken { - -Vector2::Vector2() { - x = 0.0; - y = 0.0; -} - -Vector2::Vector2(float X, float Y) { - x = X; - y = Y; -} - -Vector2::Vector2(float v) { - x = v; - y = v; -} - -Vector2::Vector2(float *v) { - x = v[0]; - y = v[1]; -} - -Vector2::Vector2(const Vector2 &v) { - x = v.x; - y = v.y; -} - - -// Vector2 swizzle getters -Vector2 Vector2::yx() const -{ - return Vector2(y,x); -} - -// Vector2 swizzle setters -void Vector2::yx(const Vector2 &v) -{ - y = v.x; - x = v.y; -} - -Vector2 Vector2::Min() { - return Vector2(-std::numeric_limits::max()); -} - -Vector2 Vector2::Max() { - return Vector2(std::numeric_limits::max()); -} - -Vector2 Vector2::Zero() { - return Vector2(0.0f); -} - -Vector2 Vector2::One() { - return Vector2(1.0f); -} - -Vector2::~Vector2() { - -} - -Vector2& Vector2::operator =(const Vector2& b) { - x = b.x; - y = b.y; - return *this; -} - -Vector2 Vector2::operator +(const Vector2& b) const { - return Vector2(x + b.x, y + b.y); -} - -Vector2 Vector2::operator -(const Vector2& b) const { - return Vector2(x - b.x, y - b.y); -} - -Vector2 Vector2::operator +() const { - return *this; -} - -Vector2 Vector2::operator -() const { - return Vector2(-x, -y); -} - -Vector2 Vector2::operator *(const float v) const { - return Vector2(x * v, y * v); -} - -Vector2 Vector2::operator /(const float v) const { - float inv_v = 1.0f / v; - return Vector2(x * inv_v, y * inv_v); -} - -Vector2& Vector2::operator +=(const Vector2& b) { - x += b.x; - y += b.y; - return *this; -} - -Vector2& Vector2::operator -=(const Vector2& b) { - x -= b.x; - y -= b.y; - return *this; -} - - - -Vector2& Vector2::operator *=(const float v) { - x *= v; - y *= v; - return *this; -} - -Vector2& Vector2::operator /=(const float v) { - float inv_v = 1.0f / v; - x *= inv_v; - y *= inv_v; - return *this; -} - - -bool Vector2::operator ==(const Vector2& b) const { - return x == b.x && y == b.y; -} - -bool Vector2::operator !=(const Vector2& b) const { - return x != b.x || y != b.y; -} - -bool Vector2::operator >(const Vector2& b) const -{ - // Comparison operators are implemented to allow insertion into sorted containers such as std::set - if(x > b.x) { - return true; - } else if(x < b.x) { - return false; - } else if(y > b.y) { - return true; - } else { - return false; - } -} - -bool Vector2::operator <(const Vector2& b) const -{ - // Comparison operators are implemented to allow insertion into sorted containers such as std::set - if(x < b.x) { - return true; - } else if(x > b.x) { - return false; - } else if(y < b.y) { - return true; - } else { - return false; - } -} - -float& Vector2::operator[] (unsigned i) { - switch(i) { - case 0: - return x; - case 1: - default: - return y; - } -} - -float Vector2::operator[](unsigned i) const { - switch(i) { - case 0: - return x; - case 1: - default: - return y; - } -} - -void Vector2::normalize() { - float inv_magnitude = 1.0f / sqrtf(x * x + y * y); - x *= inv_magnitude; - y *= inv_magnitude; -} - -float Vector2::sqrMagnitude() const { - return x * x + y * y; -} - -float Vector2::magnitude() const { - return sqrtf(x * x + y * y); -} - - -Vector2 Vector2::Normalize(const Vector2 &v) { - float inv_magnitude = 1.0f / sqrtf(v.x * v.x + v.y * v.y); - return Vector2(v.x * inv_magnitude, v.y * inv_magnitude); -} - -float Vector2::Cross(const Vector2 &v1, const Vector2 &v2) { - return v1.x * v2.y - v1.y * v2.x; -} - -float Vector2::Dot(const Vector2 &v1, const Vector2 &v2) { - return v1.x * v2.x + v1.y * v2.y; -} - -} // namepsace kraken diff --git a/kraken/vector3.cpp b/kraken/vector3.cpp deleted file mode 100644 index bfad591..0000000 --- a/kraken/vector3.cpp +++ /dev/null @@ -1,418 +0,0 @@ -// -// Vector3.cpp -// KREngine -// -// Copyright 2012 Kearwood Gilbert. All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, are -// permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of -// conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list -// of conditions and the following disclaimer in the documentation and/or other materials -// provided with the distribution. -// -// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED -// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR -// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// The views and conclusions contained in the software and documentation are those of the -// authors and should not be interpreted as representing official policies, either expressed -// or implied, of Kearwood Gilbert. -// - -#include "public/kraken.h" - -namespace kraken { - -const Vector3 Vector3_ZERO(0.0f, 0.0f, 0.0f); - -//default constructor -Vector3::Vector3() -{ - x = 0.0f; - y = 0.0f; - z = 0.0f; -} - -Vector3::Vector3(const Vector3 &v) { - x = v.x; - y = v.y; - z = v.z; -} - -Vector3::Vector3(const Vector4 &v) { - x = v.x; - y = v.y; - z = v.z; -} - -Vector3::Vector3(float *v) { - x = v[0]; - y = v[1]; - z = v[2]; -} - -Vector3::Vector3(double *v) { - x = (float)v[0]; - y = (float)v[1]; - z = (float)v[2]; -} - -Vector2 Vector3::xx() const -{ - return Vector2(x,x); -} - -Vector2 Vector3::xy() const -{ - return Vector2(x,y); -} - -Vector2 Vector3::xz() const -{ - return Vector2(x,z); -} - -Vector2 Vector3::yx() const -{ - return Vector2(y,x); -} - -Vector2 Vector3::yy() const -{ - return Vector2(y,y); -} - -Vector2 Vector3::yz() const -{ - return Vector2(y,z); -} - -Vector2 Vector3::zx() const -{ - return Vector2(z,x); -} - -Vector2 Vector3::zy() const -{ - return Vector2(z,y); -} - -Vector2 Vector3::zz() const -{ - return Vector2(z,z); -} - -void Vector3::xy(const Vector2 &v) -{ - x = v.x; - y = v.y; -} - -void Vector3::xz(const Vector2 &v) -{ - x = v.x; - z = v.y; -} - -void Vector3::yx(const Vector2 &v) -{ - y = v.x; - x = v.y; -} - -void Vector3::yz(const Vector2 &v) -{ - y = v.x; - z = v.y; -} - -void Vector3::zx(const Vector2 &v) -{ - z = v.x; - x = v.y; -} - -void Vector3::zy(const Vector2 &v) -{ - z = v.x; - y = v.y; -} - -Vector3 Vector3::Min() { - return Vector3(-std::numeric_limits::max()); -} - -Vector3 Vector3::Max() { - return Vector3(std::numeric_limits::max()); -} - -const Vector3 &Vector3::Zero() { - return Vector3_ZERO; -} - -Vector3 Vector3::One() { - return Vector3(1.0f, 1.0f, 1.0f); -} - -Vector3 Vector3::Forward() { - return Vector3(0.0f, 0.0f, 1.0f); -} - -Vector3 Vector3::Backward() { - return Vector3(0.0f, 0.0f, -1.0f); -} - -Vector3 Vector3::Up() { - return Vector3(0.0f, 1.0f, 0.0f); -} - -Vector3 Vector3::Down() { - return Vector3(0.0f, -1.0f, 0.0f); -} - -Vector3 Vector3::Left() { - return Vector3(-1.0f, 0.0f, 0.0f); -} - -Vector3 Vector3::Right() { - return Vector3(1.0f, 0.0f, 0.0f); -} - - -void Vector3::scale(const Vector3 &v) -{ - x *= v.x; - y *= v.y; - z *= v.z; -} - -Vector3 Vector3::Scale(const Vector3 &v1, const Vector3 &v2) -{ - return Vector3(v1.x * v2.x, v1.y * v2.y, v1.z * v2.z); -} - -Vector3 Vector3::Lerp(const Vector3 &v1, const Vector3 &v2, float d) { - return v1 + (v2 - v1) * d; -} - -Vector3 Vector3::Slerp(const Vector3 &v1, const Vector3 &v2, float d) { - // From: http://keithmaggio.wordpress.com/2011/02/15/math-magician-lerp-slerp-and-nlerp/ - // Dot product - the cosine of the angle between 2 vectors. - float dot = Vector3::Dot(v1, v2); - // Clamp it to be in the range of Acos() - if(dot < -1.0f) dot = -1.0f; - if(dot > 1.0f) dot = 1.0f; - // Acos(dot) returns the angle between start and end, - // And multiplying that by percent returns the angle between - // start and the final result. - float theta = acos(dot)*d; - Vector3 RelativeVec = v2 - v1*dot; - RelativeVec.normalize(); // Orthonormal basis - // The final result. - return ((v1*cos(theta)) + (RelativeVec*sin(theta))); -} - -void Vector3::OrthoNormalize(Vector3 &normal, Vector3 &tangent) { - // Gram-Schmidt Orthonormalization - normal.normalize(); - Vector3 proj = normal * Dot(tangent, normal); - tangent = tangent - proj; - tangent.normalize(); -} - -Vector3::Vector3(float v) { - x = v; - y = v; - z = v; -} - -Vector3::Vector3(float X, float Y, float Z) -{ - x = X; - y = Y; - z = Z; -} - -Vector3::~Vector3() -{ -} - -Vector3& Vector3::operator =(const Vector3& b) { - x = b.x; - y = b.y; - z = b.z; - return *this; -} - -Vector3& Vector3::operator =(const Vector4 &b) { - x = b.x; - y = b.y; - z = b.z; - return *this; -} - -Vector3 Vector3::operator +(const Vector3& b) const { - return Vector3(x + b.x, y + b.y, z + b.z); -} -Vector3 Vector3::operator -(const Vector3& b) const { - return Vector3(x - b.x, y - b.y, z - b.z); -} -Vector3 Vector3::operator +() const { - return *this; -} -Vector3 Vector3::operator -() const { - return Vector3(-x, -y, -z); -} - -Vector3 Vector3::operator *(const float v) const { - return Vector3(x * v, y * v, z * v); -} - -Vector3 Vector3::operator /(const float v) const { - float inv_v = 1.0f / v; - return Vector3(x * inv_v, y * inv_v, z * inv_v); -} - -Vector3& Vector3::operator +=(const Vector3& b) { - x += b.x; - y += b.y; - z += b.z; - - return *this; -} - -Vector3& Vector3::operator -=(const Vector3& b) { - x -= b.x; - y -= b.y; - z -= b.z; - - return *this; -} - -Vector3& Vector3::operator *=(const float v) { - x *= v; - y *= v; - z *= v; - - return *this; -} - -Vector3& Vector3::operator /=(const float v) { - float inv_v = 1.0f / v; - x *= inv_v; - y *= inv_v; - z *= inv_v; - - return *this; -} - -bool Vector3::operator ==(const Vector3& b) const { - return x == b.x && y == b.y && z == b.z; - -} -bool Vector3::operator !=(const Vector3& b) const { - return x != b.x || y != b.y || z != b.z; -} - -float& Vector3::operator[](unsigned i) { - switch(i) { - case 0: - return x; - case 1: - return y; - default: - case 2: - return z; - } -} - -float Vector3::operator[](unsigned i) const { - switch(i) { - case 0: - return x; - case 1: - return y; - case 2: - default: - return z; - } -} - -float Vector3::sqrMagnitude() const { - // calculate the square of the magnitude (useful for comparison of magnitudes without the cost of a sqrt() function) - return x * x + y * y + z * z; -} - -float Vector3::magnitude() const { - return sqrtf(x * x + y * y + z * z); -} - -void Vector3::normalize() { - float inv_magnitude = 1.0f / sqrtf(x * x + y * y + z * z); - x *= inv_magnitude; - y *= inv_magnitude; - z *= inv_magnitude; -} -Vector3 Vector3::Normalize(const Vector3 &v) { - float inv_magnitude = 1.0f / sqrtf(v.x * v.x + v.y * v.y + v.z * v.z); - return Vector3(v.x * inv_magnitude, v.y * inv_magnitude, v.z * inv_magnitude); -} - -Vector3 Vector3::Cross(const Vector3 &v1, const Vector3 &v2) { - return Vector3(v1.y * v2.z - v1.z * v2.y, - v1.z * v2.x - v1.x * v2.z, - v1.x * v2.y - v1.y * v2.x); -} - -float Vector3::Dot(const Vector3 &v1, const Vector3 &v2) { - return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z; -} - -bool Vector3::operator >(const Vector3& b) const -{ - // Comparison operators are implemented to allow insertion into sorted containers such as std::set - if(x > b.x) { - return true; - } else if(x < b.x) { - return false; - } else if(y > b.y) { - return true; - } else if(y < b.y) { - return false; - } else if(z > b.z) { - return true; - } else { - return false; - } -} - -bool Vector3::operator <(const Vector3& b) const -{ - // Comparison operators are implemented to allow insertion into sorted containers such as std::set - if(x < b.x) { - return true; - } else if(x > b.x) { - return false; - } else if(y < b.y) { - return true; - } else if(y > b.y) { - return false; - } else if(z < b.z) { - return true; - } else { - return false; - } -} - -} // namespace kraken - diff --git a/kraken/vector4.cpp b/kraken/vector4.cpp deleted file mode 100644 index b4e242e..0000000 --- a/kraken/vector4.cpp +++ /dev/null @@ -1,303 +0,0 @@ -// -// Vector4.cpp -// KREngine -// -// Copyright 2012 Kearwood Gilbert. All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, are -// permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of -// conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list -// of conditions and the following disclaimer in the documentation and/or other materials -// provided with the distribution. -// -// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED -// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR -// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// The views and conclusions contained in the software and documentation are those of the -// authors and should not be interpreted as representing official policies, either expressed -// or implied, of Kearwood Gilbert. -// - -#include "public/kraken.h" - -namespace kraken { - -const Vector4 Vector4_ZERO(0.0f, 0.0f, 0.0f, 0.0f); - -//default constructor -Vector4::Vector4() -{ - x = 0.0f; - y = 0.0f; - z = 0.0f; - w = 0.0f; -} - -Vector4::Vector4(const Vector4 &v) { - x = v.x; - y = v.y; - z = v.z; - w = v.w; -} - -Vector4::Vector4(const Vector3 &v, float W) { - x = v.x; - y = v.y; - z = v.z; - w = W; -} - -Vector4::Vector4(float *v) { - x = v[0]; - y = v[1]; - z = v[2]; - w = v[3]; -} - -Vector4 Vector4::Min() { - return Vector4(-std::numeric_limits::max()); -} - -Vector4 Vector4::Max() { - return Vector4(std::numeric_limits::max()); -} - -const Vector4 &Vector4::Zero() { - return Vector4_ZERO; -} - -Vector4 Vector4::One() { - return Vector4(1.0f, 1.0f, 1.0f, 1.0f); -} - -Vector4 Vector4::Forward() { - return Vector4(0.0f, 0.0f, 1.0f, 1.0f); -} - -Vector4 Vector4::Backward() { - return Vector4(0.0f, 0.0f, -1.0f, 1.0f); -} - -Vector4 Vector4::Up() { - return Vector4(0.0f, 1.0f, 0.0f, 1.0f); -} - -Vector4 Vector4::Down() { - return Vector4(0.0f, -1.0f, 0.0f, 1.0f); -} - -Vector4 Vector4::Left() { - return Vector4(-1.0f, 0.0f, 0.0f, 1.0f); -} - -Vector4 Vector4::Right() { - return Vector4(1.0f, 0.0f, 0.0f, 1.0f); -} - -Vector4 Vector4::Lerp(const Vector4 &v1, const Vector4 &v2, float d) { - return v1 + (v2 - v1) * d; -} - -Vector4 Vector4::Slerp(const Vector4 &v1, const Vector4 &v2, float d) { - // From: http://keithmaggio.wordpress.com/2011/02/15/math-magician-lerp-slerp-and-nlerp/ - // Dot product - the cosine of the angle between 2 vectors. - float dot = Vector4::Dot(v1, v2); - // Clamp it to be in the range of Acos() - if(dot < -1.0f) dot = -1.0f; - if(dot > 1.0f) dot = 1.0f; - // Acos(dot) returns the angle between start and end, - // And multiplying that by percent returns the angle between - // start and the final result. - float theta = acos(dot)*d; - Vector4 RelativeVec = v2 - v1*dot; - RelativeVec.normalize(); // Orthonormal basis - // The final result. - return ((v1*cos(theta)) + (RelativeVec*sin(theta))); -} - -void Vector4::OrthoNormalize(Vector4 &normal, Vector4 &tangent) { - // Gram-Schmidt Orthonormalization - normal.normalize(); - Vector4 proj = normal * Dot(tangent, normal); - tangent = tangent - proj; - tangent.normalize(); -} - -Vector4::Vector4(float v) { - x = v; - y = v; - z = v; - w = v; -} - -Vector4::Vector4(float X, float Y, float Z, float W) -{ - x = X; - y = Y; - z = Z; - w = W; -} - -Vector4::~Vector4() -{ -} - -Vector4& Vector4::operator =(const Vector4& b) { - x = b.x; - y = b.y; - z = b.z; - w = b.w; - return *this; -} -Vector4 Vector4::operator +(const Vector4& b) const { - return Vector4(x + b.x, y + b.y, z + b.z, w + b.w); -} -Vector4 Vector4::operator -(const Vector4& b) const { - return Vector4(x - b.x, y - b.y, z - b.z, w - b.w); -} -Vector4 Vector4::operator +() const { - return *this; -} -Vector4 Vector4::operator -() const { - return Vector4(-x, -y, -z, -w); -} - -Vector4 Vector4::operator *(const float v) const { - return Vector4(x * v, y * v, z * v, w * v); -} - -Vector4 Vector4::operator /(const float v) const { - return Vector4(x / v, y / v, z / v, w/ v); -} - -Vector4& Vector4::operator +=(const Vector4& b) { - x += b.x; - y += b.y; - z += b.z; - w += b.w; - - return *this; -} - -Vector4& Vector4::operator -=(const Vector4& b) { - x -= b.x; - y -= b.y; - z -= b.z; - w -= b.w; - - return *this; -} - -Vector4& Vector4::operator *=(const float v) { - x *= v; - y *= v; - z *= v; - w *= v; - - return *this; -} - -Vector4& Vector4::operator /=(const float v) { - float inv_v = 1.0f / v; - x *= inv_v; - y *= inv_v; - z *= inv_v; - w *= inv_v; - - return *this; -} - -bool Vector4::operator ==(const Vector4& b) const { - return x == b.x && y == b.y && z == b.z && w == b.w; - -} -bool Vector4::operator !=(const Vector4& b) const { - return x != b.x || y != b.y || z != b.z || w != b.w; -} - -float& Vector4::operator[](unsigned i) { - switch(i) { - case 0: - return x; - case 1: - return y; - case 2: - return z; - default: - case 3: - return w; - } -} - -float Vector4::operator[](unsigned i) const { - switch(i) { - case 0: - return x; - case 1: - return y; - case 2: - return z; - default: - case 3: - return w; - } -} - -float Vector4::sqrMagnitude() const { - // calculate the square of the magnitude (useful for comparison of magnitudes without the cost of a sqrt() function) - return x * x + y * y + z * z + w * w; -} - -float Vector4::magnitude() const { - return sqrtf(x * x + y * y + z * z + w * w); -} - -void Vector4::normalize() { - float inv_magnitude = 1.0f / sqrtf(x * x + y * y + z * z + w * w); - x *= inv_magnitude; - y *= inv_magnitude; - z *= inv_magnitude; - w *= inv_magnitude; -} -Vector4 Vector4::Normalize(const Vector4 &v) { - float inv_magnitude = 1.0f / sqrtf(v.x * v.x + v.y * v.y + v.z * v.z + v.w * v.w); - return Vector4(v.x * inv_magnitude, v.y * inv_magnitude, v.z * inv_magnitude, v.w * inv_magnitude); -} - - -float Vector4::Dot(const Vector4 &v1, const Vector4 &v2) { - return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z + v1.w * v2.w; -} - -bool Vector4::operator >(const Vector4& b) const -{ - // Comparison operators are implemented to allow insertion into sorted containers such as std::set - if(x != b.x) return x > b.x; - if(y != b.y) return y > b.y; - if(z != b.z) return z > b.z; - if(w != b.w) return w > b.w; - return false; -} - -bool Vector4::operator <(const Vector4& b) const -{ - // Comparison operators are implemented to allow insertion into sorted containers such as std::set - if(x != b.x) return x < b.x; - if(y != b.y) return y < b.y; - if(z != b.z) return z < b.z; - if(w != b.w) return w < b.w; - return false; -} - -} // namespace kraken