WIP adding directory structure

This commit is contained in:
2024-08-17 23:12:20 -07:00
parent 862ffeeaa0
commit 7e4fc361d4
34 changed files with 46 additions and 46 deletions

View File

@@ -32,7 +32,7 @@
#include "KREngine-common.h"
#include "KRResource.h"
#include "KRMesh.h"
#include "mesh/KRMesh.h"
#include "mimir.h"

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,258 @@
//
// FileManager.h
// Kraken Engine
//
// Copyright 2024 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.
//
#pragma once
#include "KREngine-common.h"
#include "resources/KRResourceManager.h"
#include "KRContextObject.h"
#include "block.h"
#include "KRAudioSource.h"
#include "siren.h"
const int KRENGINE_AUDIO_MAX_POOL_SIZE = 60; //32;
// for Circa we play a maximum of 11 mono audio streams at once + cross fading with ambient
// so we could safely say a maximum of 12 or 13 streams, which would be 39 buffers
// do the WAV files for the reverb use the same buffer pool ???
const int KRENGINE_AUDIO_MAX_BUFFER_SIZE = 5120; // in bytes
// this is the buffer for our decoded audio (not the source file data)
// it should be greater then 1152 samples (the size of an mp3 frame in samples)
// so it should be greater then 2304 bytes and also a multiple of 128 samples (to make
// the data flow efficient) but it shouldn't be too large or it will cause
// the render loop to stall out decoding large chunks of mp3 data.
// 2560 bytes would be the smallest size for mono sources, and 5120 would be smallest for stereo.
const int KRENGINE_AUDIO_BUFFERS_PER_SOURCE = 3;
const int KRENGINE_AUDIO_BLOCK_LOG2N = 7; // 2 ^ KRENGINE_AUDIO_BLOCK_LOG2N = KRENGINE_AUDIO_BLOCK_LENGTH
// 7 is 128 .. NOTE: the hrtf code uses magic numbers everywhere and is hardcoded to 128 samples per frame
const int KRENGINE_AUDIO_BLOCK_LENGTH = 1 << KRENGINE_AUDIO_BLOCK_LOG2N;
// Length of one block to process. Determines the latency of the audio system and sets size for FFT's used in HRTF convolution
// the AUGraph works in 1024 sample chunks. At 128 we are making 8 consecutive calls to the renderBlock method for each
// render initiated by the AUGraph.
const int KRENGINE_REVERB_MAX_FFT_LOG2 = 15;
const int KRENGINE_REVERB_WORKSPACE_SIZE = 1 << KRENGINE_REVERB_MAX_FFT_LOG2;
const float KRENGINE_AUDIO_CUTOFF = 0.02f; // Cutoff gain level, to cull out processing of very quiet sounds
const int KRENGINE_REVERB_MAX_SAMPLES = 128000; // 2.9 seconds //435200; // At least 10s reverb impulse response length, divisible by KRENGINE_AUDIO_BLOCK_LENGTH
const int KRENGINE_MAX_REVERB_IMPULSE_MIX = 8; // Maximum number of impulse response filters that can be mixed simultaneously
const int KRENGINE_MAX_OUTPUT_CHANNELS = 2;
const int KRENGINE_MAX_ACTIVE_SOURCES = 16;
const int KRENGINE_AUDIO_ANTICLICK_SAMPLES = 64;
class KRAmbientZone;
class KRReverbZone;
typedef struct
{
float weight;
KRAmbientZone* ambient_zone;
KRAudioSample* ambient_sample;
} siren_ambient_zone_weight_info;
typedef struct
{
float weight;
KRReverbZone* reverb_zone;
KRAudioSample* reverb_sample;
} siren_reverb_zone_weight_info;
class KRAudioManager : public KRResourceManager
{
public:
KRAudioManager(KRContext& context);
virtual ~KRAudioManager();
void destroy();
virtual KRResource* loadResource(const std::string& name, const std::string& extension, mimir::Block* data) override;
virtual KRResource* getResource(const std::string& name, const std::string& extension) override;
unordered_map<std::string, KRAudioSample*>& getSounds();
void add(KRAudioSample* Sound);
KRAudioSample* load(const std::string& name, const std::string& extension, mimir::Block* data);
KRAudioSample* get(const std::string& name);
// Listener position and orientation
KRScene* getListenerScene();
void setListenerScene(KRScene* scene);
void setListenerOrientation(const hydra::Vector3& position, const hydra::Vector3& forward, const hydra::Vector3& up);
void setListenerOrientationFromModelMatrix(const hydra::Matrix4& modelMatrix);
hydra::Vector3& getListenerForward();
hydra::Vector3& getListenerPosition();
hydra::Vector3& getListenerUp();
// Global audio gain / attenuation
float getGlobalGain();
void setGlobalGain(float gain);
float getGlobalReverbSendLevel();
void setGlobalReverbSendLevel(float send_level);
float getGlobalAmbientGain();
void setGlobalAmbientGain(float gain);
void makeCurrentContext();
mimir::Block* getBufferData(int size);
void recycleBufferData(mimir::Block* data);
void activateAudioSource(KRAudioSource* audioSource);
void deactivateAudioSource(KRAudioSource* audioSource);
__int64_t getAudioFrame();
KRAudioBuffer* getBuffer(KRAudioSample& audio_sample, int buffer_index);
static void mute(bool onNotOff);
void goToSleep();
void startFrame(float deltaTime);
bool getEnableAudio();
void setEnableAudio(bool enable);
bool getEnableHRTF();
void setEnableHRTF(bool enable);
bool getEnableReverb();
void setEnableReverb(bool enable);
float getReverbMaxLength();
void setReverbMaxLength(float max_length);
void _registerOpenAudioSample(KRAudioSample* audioSample);
void _registerCloseAudioSample(KRAudioSample* audioSample);
private:
bool m_enable_audio;
bool m_enable_hrtf;
bool m_enable_reverb;
float m_reverb_max_length;
KRScene* m_listener_scene; // For now, only one scene is allowed to have active audio at once
float m_global_reverb_send_level;
float m_global_ambient_gain;
float m_global_gain;
hydra::Vector3 m_listener_position;
hydra::Vector3 m_listener_forward;
hydra::Vector3 m_listener_up;
unordered_map<std::string, KRAudioSample*> m_sounds;
std::vector<mimir::Block*> m_bufferPoolIdle;
std::vector<KRAudioBuffer*> m_bufferCache;
std::set<KRAudioSource*> m_activeAudioSources;
std::set<KRAudioSample*> m_openAudioSamples;
void initAudio();
void initHRTF();
void cleanupAudio();
bool m_initialized;
#ifdef __APPLE__
// Apple Core Audio
AUGraph m_auGraph;
AudioUnit m_auMixer;
static OSStatus renderInput(void* inRefCon, AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData);
void renderAudio(UInt32 inNumberFrames, AudioBufferList* ioData);
#endif
siren::dsp::FFTWorkspace m_fft_setup[KRENGINE_REVERB_MAX_FFT_LOG2 - KRENGINE_AUDIO_BLOCK_LOG2N + 1];
__int64_t m_audio_frame; // Number of audio frames processed since the start of the application
float* m_reverb_input_samples; // Circular-buffered reverb input, single channel
int m_reverb_input_next_sample; // Pointer to next sample in reverb buffer
int m_reverb_sequence;
KRAudioSample* m_reverb_impulse_responses[KRENGINE_MAX_REVERB_IMPULSE_MIX];
float m_reverb_impulse_responses_weight[KRENGINE_MAX_REVERB_IMPULSE_MIX];
float* m_output_accumulation; // Interleaved output accumulation buffer
int m_output_accumulation_block_start;
int m_output_sample;
float* m_workspace_data;
siren::dsp::SplitComplex m_workspace[3];
float* getBlockAddress(int block_offset);
void renderBlock();
void renderReverb();
void renderAmbient();
void renderHRTF();
void renderITD();
void renderReverbImpulseResponse(int impulse_response_offset, int frame_count_log2);
void renderLimiter();
std::vector<hydra::Vector2> m_hrtf_sample_locations;
float* m_hrtf_data;
unordered_map<hydra::Vector2, siren::dsp::SplitComplex> m_hrtf_spectral[2];
hydra::Vector2 getNearestHRTFSample(const hydra::Vector2& dir);
void getHRTFMix(const hydra::Vector2& dir, hydra::Vector2& hrtf1, hydra::Vector2& hrtf2, hydra::Vector2& hrtf3, hydra::Vector2& hrtf4, float& mix1, float& mix2, float& mix3, float& mix4);
KRAudioSample* getHRTFSample(const hydra::Vector2& hrtf_dir);
siren::dsp::SplitComplex getHRTFSpectral(const hydra::Vector2& hrtf_dir, const int channel);
unordered_map<std::string, siren_ambient_zone_weight_info> m_ambient_zone_weights;
float m_ambient_zone_total_weight = 0.0f; // For normalizing zone weights
unordered_map<std::string, siren_reverb_zone_weight_info> m_reverb_zone_weights;
float m_reverb_zone_total_weight = 0.0f; // For normalizing zone weights
std::mutex m_mutex;
#ifdef __APPLE__
mach_timebase_info_data_t m_timebase_info;
#endif
unordered_multimap<hydra::Vector2, std::pair<KRAudioSource*, std::pair<float, float> > > m_mapped_sources, m_prev_mapped_sources;
bool m_anticlick_block;
bool m_high_quality_hrtf; // If true, 4 HRTF samples will be interpolated; if false, the nearest HRTF sample will be used without interpolation
};

View File

@@ -0,0 +1,396 @@
//
// KRAudioSample.cpp
// Kraken Engine
//
// Copyright 2024 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 "KRAudioSample.h"
#include "KRAudioManager.h"
#include "block.h"
#include "KRAudioBuffer.h"
#include "KRContext.h"
#include "siren.h"
using namespace mimir;
using namespace siren;
KRAudioSample::KRAudioSample(KRContext& context, std::string name, std::string extension) : KRResource(context, name)
{
m_pData = new Block();
m_extension = extension;
#ifdef __APPLE__
// Apple Audio Toolbox
m_audio_file_id = 0;
m_fileRef = NULL;
#endif
m_totalFrames = 0;
m_bytesPerFrame = 0;
m_frameRate = 0;
m_bufferCount = 0;
m_last_frame_used = 0;
}
KRAudioSample::KRAudioSample(KRContext& context, std::string name, std::string extension, Block* data) : KRResource(context, name)
{
m_pData = data;
m_extension = extension;
#ifdef __APPLE__
// Apple Audio Toolbox
m_audio_file_id = 0;
m_fileRef = NULL;
#endif
m_totalFrames = 0;
m_bytesPerFrame = 0;
m_frameRate = 0;
m_bufferCount = 0;
m_last_frame_used = 0;
}
KRAudioSample::~KRAudioSample()
{
closeFile();
delete m_pData;
}
int KRAudioSample::getChannelCount()
{
loadInfo();
return m_channelsPerFrame;
}
__int64_t KRAudioSample::getFrameCount()
{
loadInfo();
//return (int)((__int64_t)m_totalFrames * (__int64_t)frame_rate / (__int64_t)m_frameRate);
return m_totalFrames;
}
float KRAudioSample::sample(int frame_offset, int frame_rate, int channel)
{
loadInfo();
int c = KRMIN(channel, m_channelsPerFrame - 1);
if (frame_offset < 0) {
return 0.0f; // Past the beginning of the recording
} else {
int sample_frame;
if (m_frameRate == frame_rate) {
// No resampling required
sample_frame = frame_offset;
} else {
// Need to resample from m_frameRate to frame_rate
sample_frame = (int)((__int64_t)frame_offset * (__int64_t)m_frameRate / (__int64_t)frame_rate);
}
int maxFramesPerBuffer = KRENGINE_AUDIO_MAX_BUFFER_SIZE / m_bytesPerFrame;
int buffer_index = sample_frame / maxFramesPerBuffer;
if (buffer_index >= m_bufferCount) {
return 0.0f; // Past the end of the recording
} else {
__int64_t buffer_offset = frame_offset - buffer_index * maxFramesPerBuffer;
KRAudioBuffer* buffer = getContext().getAudioManager()->getBuffer(*this, buffer_index);
if (buffer == NULL) {
return 0.0f;
} else if (buffer_offset >= buffer->getFrameCount()) {
return 0.0f; // past the end of the recording
} else {
short* frame = buffer->getFrameData() + (buffer_offset * m_channelsPerFrame);
return frame[c] / 32767.0f;
}
}
}
}
void KRAudioSample::sample(__int64_t frame_offset, int frame_count, int channel, float* buffer, float amplitude, bool loop)
{
loadInfo();
m_last_frame_used = (int)getContext().getAudioManager()->getAudioFrame();
if (loop) {
int buffer_offset = 0;
int frames_left = frame_count;
int sample_length = (int)getFrameCount();
while (frames_left) {
int next_frame = (int)(((__int64_t)frame_offset + (__int64_t)buffer_offset) % sample_length);
if (next_frame + frames_left >= sample_length) {
int frames_processed = sample_length - next_frame;
sample(next_frame, frames_processed, channel, buffer + buffer_offset, amplitude, false);
frames_left -= frames_processed;
buffer_offset += frames_processed;
} else {
sample(next_frame, frames_left, channel, buffer + buffer_offset, amplitude, false);
frames_left = 0;
}
}
} else {
int c = KRMIN(channel, m_channelsPerFrame - 1);
if (frame_offset + frame_count <= 0) {
// Range is entirely before the sample
memset(buffer, 0, frame_count * sizeof(float));
} else if (frame_offset >= m_totalFrames) {
// Range is entirely after the sample
memset(buffer, 0, frame_count * sizeof(float));
} else {
int start_frame = (int)(frame_offset < 0 ? 0 : frame_offset);
int prefix_frames = (int)(frame_offset < 0 ? -frame_offset : 0);
if (prefix_frames > 0) {
// Prefix with padding of 0's
memset(buffer, 0, prefix_frames * sizeof(float));
}
int frames_per_buffer = KRENGINE_AUDIO_MAX_BUFFER_SIZE / m_bytesPerFrame;
int buffer_index = start_frame / frames_per_buffer;
int buffer_offset = start_frame % frames_per_buffer;
int processed_frames = prefix_frames;
while (processed_frames < frame_count) {
int frames_left = frame_count - processed_frames;
if (buffer_index >= m_bufferCount) {
// Suffix with padding of 0's
memset(buffer + processed_frames, 0, frames_left * sizeof(float));
processed_frames += frames_left;
} else {
KRAudioBuffer* source_buffer = getContext().getAudioManager()->getBuffer(*this, buffer_index);
int frames_to_copy = source_buffer->getFrameCount() - buffer_offset;
if (frames_to_copy > frames_left) frames_to_copy = frames_left;
if (frames_to_copy > 0) {
signed short* source_data = source_buffer->getFrameData() + buffer_offset * m_channelsPerFrame + c;
siren::dsp::Int16ToFloat(source_data, m_channelsPerFrame, buffer + processed_frames, 1, frames_to_copy);
//memcpy(buffer + processed_frames, source_buffer->getFrameData() + buffer_offset, frames_to_copy * m_channelsPerFrame * sizeof(float));
processed_frames += frames_to_copy;
}
buffer_index++;
buffer_offset = 0;
}
}
}
float scale = amplitude / 32768.0f;
siren::dsp::Scale(buffer, scale, frame_count);
}
}
#ifdef __APPLE__
// Apple Audio Toolbox
OSStatus KRAudioSample::ReadProc( // AudioFile_ReadProc
void* inClientData,
SInt64 inPosition,
UInt32 requestCount,
void* buffer,
UInt32* actualCount)
{
KRAudioSample* sound = (KRAudioSample*)inClientData;
UInt32 max_count = sound->m_pData->getSize() - inPosition;
*actualCount = requestCount < max_count ? requestCount : max_count;
sound->m_pData->copy(buffer, inPosition, *actualCount);
return noErr;
}
SInt64 KRAudioSample::GetSizeProc( // AudioFile_GetSizeProc
void* inClientData)
{
KRAudioSample* sound = (KRAudioSample*)inClientData;
return sound->m_pData->getSize();
}
OSStatus KRAudioSample::SetSizeProc( // AudioFile_SetSizeProc
void* inClientData,
SInt64 inSize)
{
return -1; // Writing not supported
}
OSStatus KRAudioSample::WriteProc( // AudioFile_WriteProc
void* inClientData,
SInt64 inPosition,
UInt32 requestCount,
const void* buffer,
UInt32* actualCount)
{
return -1; // Writing not supported
}
#endif // Apple Audio Toolbox
void KRAudioSample::openFile()
{
#ifdef __APPLE__
// Apple Audio Toolbox
// AudioFileInitializeWithCallbacks
if (m_fileRef == NULL) {
// printf("Call to KRAudioSample::openFile() with extension: %s\n", m_extension.c_str());
// The m_extension is valid (it's either wav or mp3 for the files in Circa project)
// so we can key off the extension and use a different data handler for mp3 files if we want to
//
// Temp variables
UInt32 propertySize;
// ---- Open audio file ----
assert(AudioFileOpenWithCallbacks((void*)this, ReadProc, WriteProc, GetSizeProc, SetSizeProc, 0, &m_audio_file_id) == noErr);
assert(ExtAudioFileWrapAudioFileID(m_audio_file_id, false, &m_fileRef) == noErr);
// ---- Get file format information ----
AudioStreamBasicDescription inputFormat;
propertySize = sizeof(inputFormat);
ExtAudioFileGetProperty(m_fileRef, kExtAudioFileProperty_FileDataFormat, &propertySize, &inputFormat);
// ---- Set up output format ----
AudioStreamBasicDescription outputFormat;
// Set the client format to 16 bit signed integer (native-endian) data
// Maintain the channel count and sample rate of the original source format
outputFormat.mSampleRate = inputFormat.mSampleRate;
outputFormat.mChannelsPerFrame = inputFormat.mChannelsPerFrame;
outputFormat.mFormatID = kAudioFormatLinearPCM;
outputFormat.mBytesPerPacket = 2 * outputFormat.mChannelsPerFrame;
outputFormat.mFramesPerPacket = 1;
outputFormat.mBytesPerFrame = 2 * outputFormat.mChannelsPerFrame;
outputFormat.mBitsPerChannel = 16;
outputFormat.mFormatFlags = kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked | kAudioFormatFlagIsSignedInteger;
ExtAudioFileSetProperty(m_fileRef, kExtAudioFileProperty_ClientDataFormat, sizeof(outputFormat), &outputFormat);
// ---- Get the buffer size and format parameters ----
propertySize = sizeof(m_totalFrames);
ExtAudioFileGetProperty(m_fileRef, kExtAudioFileProperty_FileLengthFrames, &propertySize, &m_totalFrames);
m_bytesPerFrame = outputFormat.mBytesPerFrame;
m_frameRate = outputFormat.mSampleRate;
int maxFramesPerBuffer = KRENGINE_AUDIO_MAX_BUFFER_SIZE / m_bytesPerFrame;
m_bufferCount = (m_totalFrames + maxFramesPerBuffer - 1) / maxFramesPerBuffer; // CEIL(_totalFrames / maxFramesPerBuffer)
m_channelsPerFrame = outputFormat.mChannelsPerFrame;
getContext().getAudioManager()->_registerOpenAudioSample(this);
}
#else
#pragma message ( "TODO - implement for Windows" )
#endif
}
void KRAudioSample::closeFile()
{
#ifdef __APPLE__
// Apple Audio Toolbox
if (m_fileRef) {
ExtAudioFileDispose(m_fileRef);
m_fileRef = NULL;
}
if (m_audio_file_id) {
AudioFileClose(m_audio_file_id);
m_audio_file_id = 0;
}
#endif
getContext().getAudioManager()->_registerCloseAudioSample(this);
}
void KRAudioSample::loadInfo()
{
if (m_frameRate == 0) {
openFile();
closeFile();
}
}
std::string KRAudioSample::getExtension()
{
return m_extension;
}
bool KRAudioSample::save(Block& data)
{
data.append(*m_pData);
return true;
}
float KRAudioSample::getDuration()
{
loadInfo();
return (float)m_totalFrames / (float)m_frameRate;
}
int KRAudioSample::getBufferCount()
{
loadInfo();
return m_bufferCount;
}
void KRAudioSample::PopulateBuffer(KRAudioSample* sound, int index, void* data)
{
int maxFramesPerBuffer = KRENGINE_AUDIO_MAX_BUFFER_SIZE / sound->m_bytesPerFrame;
int startFrame = index * maxFramesPerBuffer;
__uint32_t frameCount = (__uint32_t)KRMIN(sound->m_totalFrames - startFrame, maxFramesPerBuffer);
#ifdef __APPLE__
// Apple Audio Toolbox
AudioBufferList outputBufferInfo;
outputBufferInfo.mNumberBuffers = 1;
outputBufferInfo.mBuffers[0].mDataByteSize = frameCount * sound->m_bytesPerFrame;
outputBufferInfo.mBuffers[0].mNumberChannels = sound->m_channelsPerFrame;
outputBufferInfo.mBuffers[0].mData = data;
// Read the data into an AudioBufferList
ExtAudioFileSeek(sound->m_fileRef, startFrame);
ExtAudioFileRead(sound->m_fileRef, (UInt32*)&frameCount, &outputBufferInfo);
#endif
}
KRAudioBuffer* KRAudioSample::getBuffer(int index)
{
openFile();
int maxFramesPerBuffer = KRENGINE_AUDIO_MAX_BUFFER_SIZE / m_bytesPerFrame;
int startFrame = index * maxFramesPerBuffer;
__uint32_t frameCount = (__uint32_t)KRMIN(m_totalFrames - startFrame, maxFramesPerBuffer);
KRAudioBuffer* buffer = new KRAudioBuffer(getContext().getAudioManager(), this, index, frameCount, m_frameRate, m_bytesPerFrame, PopulateBuffer);
if (m_bufferCount == 1) {
// [self closeFile]; // We don't need to hold on to a file handle if not streaming
}
return buffer;
}
void KRAudioSample::_endFrame()
{
const __int64_t AUDIO_SAMPLE_EXPIRY_FRAMES = 500;
__int64_t current_frame = getContext().getAudioManager()->getAudioFrame();
if (current_frame > m_last_frame_used + AUDIO_SAMPLE_EXPIRY_FRAMES) {
closeFile();
}
}

View File

@@ -0,0 +1,111 @@
//
// KRAudioSample.h
// Kraken Engine
//
// Copyright 2024 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.
//
#pragma once
#include "KREngine-common.h"
#include "KRContextObject.h"
#include "block.h"
#include "resources/KRResource.h"
class KRAudioBuffer;
class KRAudioSample : public KRResource
{
public:
KRAudioSample(KRContext& context, std::string name, std::string extension);
KRAudioSample(KRContext& context, std::string name, std::string extension, mimir::Block* data);
virtual ~KRAudioSample();
virtual std::string getExtension();
virtual bool save(mimir::Block& data);
float getDuration();
KRAudioBuffer* getBuffer(int index);
int getBufferCount();
// Siren audio engine interface
int getChannelCount();
__int64_t getFrameCount();
float sample(int frame_offset, int frame_rate, int channel);
void sample(__int64_t frame_offset, int frame_count, int channel, float* buffer, float amplitude, bool loop);
void _endFrame();
private:
__int64_t m_last_frame_used;
std::string m_extension;
mimir::Block* m_pData;
#ifdef __APPLE__
// Apple Audio Toolbox
AudioFileID m_audio_file_id;
ExtAudioFileRef m_fileRef;
static OSStatus ReadProc( // AudioFile_ReadProc
void* inClientData,
SInt64 inPosition,
UInt32 requestCount,
void* buffer,
UInt32* actualCount);
static OSStatus WriteProc( // AudioFile_WriteProc
void* inClientData,
SInt64 inPosition,
UInt32 requestCount,
const void* buffer,
UInt32* actualCount);
static SInt64 GetSizeProc( // AudioFile_GetSizeProc
void* inClientData);
static OSStatus SetSizeProc( // AudioFile_SetSizeProc
void* inClientData,
SInt64 inSize);
#endif
int m_bufferCount;
__int64_t m_totalFrames;
int m_frameRate;
int m_bytesPerFrame;
int m_channelsPerFrame;
void openFile();
void closeFile();
void loadInfo();
static void PopulateBuffer(KRAudioSample* sound, int index, void* data);
};

1693
kraken/resources/mesh/KRMesh.cpp Executable file

File diff suppressed because it is too large Load Diff

307
kraken/resources/mesh/KRMesh.h Executable file
View File

@@ -0,0 +1,307 @@
//
// KRMesh.h
// Kraken Engine
//
// Copyright 2024 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.
//
#pragma once
#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))
#include "resources/material/KRMaterialManager.h"
#include "KRCamera.h"
#include "KRViewport.h"
class KRMaterial;
class KRNode;
class KRRenderPass;
enum class ModelFormat : __uint8_t
{
KRENGINE_MODEL_FORMAT_TRIANGLES = 0,
KRENGINE_MODEL_FORMAT_STRIP,
KRENGINE_MODEL_FORMAT_INDEXED_TRIANGLES,
KRENGINE_MODEL_FORMAT_INDEXED_STRIP
};
class KRMesh : public KRResource
{
public:
static void parseName(const std::string& name, std::string& lodBaseName, int& lodCoverage);
KRMesh(KRContext& context, std::string name, mimir::Block* 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 struct
{
ModelFormat format;
std::vector<hydra::Vector3> vertices;
std::vector<__uint16_t> vertex_indexes;
std::vector<std::pair<int, int> > vertex_index_bases;
std::vector<hydra::Vector2> uva;
std::vector<hydra::Vector2> uvb;
std::vector<hydra::Vector3> normals;
std::vector<hydra::Vector3> tangents;
std::vector<int> submesh_starts;
std::vector<int> submesh_lengths;
std::vector<std::string> material_names;
std::vector<std::string> bone_names;
std::vector<std::vector<int> > bone_indexes;
std::vector<hydra::Matrix4> bone_bind_poses;
std::vector<std::vector<float> > bone_weights;
} mesh_info;
void render(const KRNode::RenderInfo& ri, const std::string& object_name, const hydra::Matrix4& matModel, KRTexture* pLightMap, const std::vector<KRBone*>& bones, const hydra::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(mimir::Block& data);
void LoadData(const mesh_info& mi, bool calculate_normals, bool calculate_tangents);
void loadPack(mimir::Block* data);
void convertToIndexed();
void optimize();
void optimizeIndexes();
void renderNoMaterials(VkCommandBuffer& commandBuffer, const KRRenderPass* renderPass, const std::string& object_name, const std::string& material_name, float lodCoverage);
bool isReady() const;
float getMaxDimension();
hydra::Vector3 getMinPoint() const;
hydra::Vector3 getMaxPoint() const;
class Submesh
{
public:
Submesh()
{};
~Submesh()
{
vbo_data_blocks.clear();
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);
}
};
int start_vertex;
int vertex_count;
char szMaterialName[KRENGINE_MAX_NAME_LENGTH];
vector<mimir::Block*> vertex_data_blocks;
vector<mimir::Block*> index_data_blocks;
// KRMeshManager depends on the address of KRVBOData's being constant
// after allocation, enforced by deleted copy constructors.
// As std::vector requires copy constuctors, we wrap these in shared_ptr.
vector<shared_ptr<KRMeshManager::KRVBOData>> 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;
__uint32_t getVertexAttributes() const;
int getTriangleVertexIndex(int submesh, int index) const;
hydra::Vector3 getVertexPosition(int index) const;
hydra::Vector3 getVertexNormal(int index) const;
hydra::Vector3 getVertexTangent(int index) const;
hydra::Vector2 getVertexUVA(int index) const;
hydra::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 hydra::Vector3& v);
void setVertexNormal(int index, const hydra::Vector3& v);
void setVertexTangent(int index, const hydra::Vector3& v);
void setVertexUVA(int index, const hydra::Vector2& v);
void setVertexUVB(int index, const hydra::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);
static VkFormat AttributeVulkanFormat(__int32_t vertex_attrib);
int getBoneCount();
char* getBoneName(int bone_index);
hydra::Matrix4 getBoneBindPose(int bone_index);
ModelFormat getModelFormat() const;
bool lineCast(const hydra::Vector3& v0, const hydra::Vector3& v1, hydra::HitInfo& hitinfo) const;
bool rayCast(const hydra::Vector3& v0, const hydra::Vector3& dir, hydra::HitInfo& hitinfo) const;
bool sphereCast(const hydra::Matrix4& model_to_world, const hydra::Vector3& v0, const hydra::Vector3& v1, float radius, hydra::HitInfo& hitinfo) const;
static int GetLODCoverage(const std::string& name);
protected:
bool m_constant; // TRUE if this should be always loaded and should not be passed through the streamer
private:
mimir::Block* m_pData;
mimir::Block* m_pMetaData;
mimir::Block* m_pIndexBaseData;
void getSubmeshes();
void getMaterials();
void renderSubmesh(VkCommandBuffer& commandBuffer, int iSubmesh, const KRRenderPass* renderPass, const std::string& object_name, const std::string& material_name, float lodCoverage);
static bool rayCast(const hydra::Vector3& start, const hydra::Vector3& dir, const hydra::Triangle3& tri, const hydra::Vector3& tri_n0, const hydra::Vector3& tri_n1, const hydra::Vector3& tri_n2, hydra::HitInfo& hitinfo);
static bool sphereCast(const hydra::Matrix4& model_to_world, const hydra::Vector3& v0, const hydra::Vector3& v1, float radius, const hydra::Triangle3& tri, hydra::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<KRMaterial*> m_materials;
set<KRMaterial*> m_uniqueMaterials;
bool m_hasTransparency;
hydra::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<Submesh> 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);
};

View File

@@ -0,0 +1,70 @@
//
// KRMeshCube.cpp
// Kraken Engine
//
// Copyright 2024 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"
using namespace hydra;
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((int)mi.vertices.size());
mi.material_names.push_back("");
mi.format = ModelFormat::KRENGINE_MODEL_FORMAT_STRIP;
LoadData(mi, true, true);
}
KRMeshCube::~KRMeshCube()
{
}

View File

@@ -0,0 +1,43 @@
//
// KRMeshCube.h
// Kraken Engine
//
// Copyright 2024 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.
//
#pragma once
#include "KRMesh.h"
class KRMeshCube : public KRMesh
{
public:
KRMeshCube(KRContext& context);
virtual ~KRMeshCube();
private:
};

View File

@@ -0,0 +1,715 @@
//
// KRMeshManager.cpp
// Kraken Engine
//
// Copyright 2024 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 "KRMeshManager.h"
#include "KRMesh.h"
#include "KRMeshCube.h"
#include "KRMeshQuad.h"
#include "KRMeshSphere.h"
#include "KRRenderPass.h"
using namespace mimir;
KRMeshManager::KRMeshManager(KRContext& context)
: KRResourceManager(context)
, m_currentVBO(NULL)
, m_vboMemUsed(0)
, m_memoryTransferredThisFrame(0)
, m_streamerComplete(true)
, m_draw_call_logging_enabled(false)
, m_draw_call_log_used(false)
{
}
void KRMeshManager::init()
{
addModel(new KRMeshCube(*m_pContext));
addModel(new KRMeshQuad(*m_pContext));
addModel(new KRMeshSphere(*m_pContext));
// ---- Initialize stock models ----
static const float _KRENGINE_VBO_3D_CUBE_VERTEX_DATA[] = {
1.0, 1.0, 1.0,
-1.0, 1.0, 1.0,
1.0,-1.0, 1.0,
-1.0,-1.0, 1.0,
-1.0,-1.0,-1.0,
-1.0, 1.0, 1.0,
-1.0, 1.0,-1.0,
1.0, 1.0, 1.0,
1.0, 1.0,-1.0,
1.0,-1.0, 1.0,
1.0,-1.0,-1.0,
-1.0,-1.0,-1.0,
1.0, 1.0,-1.0,
-1.0, 1.0,-1.0
};
KRENGINE_VBO_3D_CUBE_ATTRIBS = (1 << KRMesh::KRENGINE_ATTRIB_VERTEX);
KRENGINE_VBO_3D_CUBE_VERTICES.expand(sizeof(float) * 3 * 14);
KRENGINE_VBO_3D_CUBE_VERTICES.lock();
memcpy(KRENGINE_VBO_3D_CUBE_VERTICES.getStart(), _KRENGINE_VBO_3D_CUBE_VERTEX_DATA, sizeof(float) * 3 * 14);
KRENGINE_VBO_3D_CUBE_VERTICES.unlock();
KRENGINE_VBO_DATA_3D_CUBE_VERTICES.init(this, &KRENGINE_VBO_3D_CUBE_VERTICES, nullptr, KRENGINE_VBO_3D_CUBE_ATTRIBS, false, KRVBOData::CONSTANT
#if KRENGINE_DEBUG_GPU_LABELS
, "Cube Mesh [built-in]"
#endif
);
initRandomParticles();
initVolumetricLightingVertexes();
static const float _KRENGINE_VBO_2D_SQUARE_VERTEX_DATA[] = {
-1.0f, -1.0f, 0.0f, 0.0f, 0.0f,
1.0f, -1.0f, 0.0f, 1.0f, 0.0f,
-1.0f, 1.0f, 0.0f, 0.0f, 1.0f,
1.0f, 1.0f, 0.0f, 1.0f, 1.0f
};
KRENGINE_VBO_2D_SQUARE_ATTRIBS = (1 << KRMesh::KRENGINE_ATTRIB_VERTEX) | (1 << KRMesh::KRENGINE_ATTRIB_TEXUVA);
KRENGINE_VBO_2D_SQUARE_VERTICES.expand(sizeof(float) * 5 * 4);
KRENGINE_VBO_2D_SQUARE_VERTICES.lock();
memcpy(KRENGINE_VBO_2D_SQUARE_VERTICES.getStart(), _KRENGINE_VBO_2D_SQUARE_VERTEX_DATA, sizeof(float) * 5 * 4);
KRENGINE_VBO_2D_SQUARE_VERTICES.unlock();
KRENGINE_VBO_DATA_2D_SQUARE_VERTICES.init(this, &KRENGINE_VBO_2D_SQUARE_VERTICES, nullptr, KRENGINE_VBO_2D_SQUARE_ATTRIBS, false, KRVBOData::CONSTANT
#if KRENGINE_DEBUG_GPU_LABELS
, "Square Mesh [built-in]"
#endif
);
}
KRMeshManager::~KRMeshManager()
{
for (unordered_multimap<std::string, KRMesh*>::iterator itr = m_models.begin(); itr != m_models.end(); ++itr) {
delete (*itr).second;
}
m_models.clear();
}
KRResource* KRMeshManager::loadResource(const std::string& name, const std::string& extension, Block* data)
{
if (extension.compare("krmesh") == 0) {
return loadModel(name.c_str(), data);
}
return nullptr;
}
KRResource* KRMeshManager::getResource(const std::string& name, const std::string& extension)
{
if (extension.compare("krmesh") == 0) {
std::string lodBaseName;
int lodCoverage;
KRMesh::parseName(name, lodBaseName, lodCoverage);
std::vector<KRMesh*> models = getModel(lodBaseName.c_str());
for (KRMesh* mesh : models) {
if (mesh->getLODCoverage() == lodCoverage) {
return mesh;
}
}
}
return nullptr;
}
KRMesh* KRMeshManager::loadModel(const char* szName, Block* pData)
{
KRMesh* pModel = new KRMesh(*m_pContext, szName, pData);
addModel(pModel);
return pModel;
}
void KRMeshManager::addModel(KRMesh* model)
{
std::string lowerName = model->getLODBaseName();
std::transform(lowerName.begin(), lowerName.end(),
lowerName.begin(), ::tolower);
m_models.insert(std::pair<std::string, KRMesh*>(lowerName, model));
}
KRMesh* KRMeshManager::getMaxLODModel(const char* szName)
{
std::vector<KRMesh*> models = getModel(szName);
// models are always in order of highest LOD first
if (models.size()) {
return models[0];
}
return nullptr;
}
std::vector<KRMesh*> KRMeshManager::getModel(const char* szName)
{
std::string lowerName = szName;
std::transform(lowerName.begin(), lowerName.end(),
lowerName.begin(), ::tolower);
std::vector<KRMesh*> matching_models;
std::pair<unordered_multimap<std::string, KRMesh*>::iterator, unordered_multimap<std::string, KRMesh*>::iterator> range = m_models.equal_range(lowerName);
for (unordered_multimap<std::string, KRMesh*>::iterator itr_match = range.first; itr_match != range.second; itr_match++) {
matching_models.push_back(itr_match->second);
}
std::sort(matching_models.begin(), matching_models.end(), KRMesh::lod_sort_predicate);
if (matching_models.size() == 0) {
KRContext::Log(KRContext::LOG_LEVEL_INFORMATION, "Model not found: %s", lowerName.c_str());
}
return matching_models;
}
unordered_multimap<std::string, KRMesh*>& KRMeshManager::getModels()
{
return m_models;
}
void KRMeshManager::bindVBO(VkCommandBuffer& commandBuffer, KRVBOData* vbo_data, float lodCoverage)
{
vbo_data->resetPoolExpiry(lodCoverage);
bool vbo_changed = false;
if (m_currentVBO == NULL) {
vbo_changed = true;
} else if (m_currentVBO->m_data != vbo_data->m_data) {
vbo_changed = true;
}
if (vbo_changed) {
if (m_vbosActive.find(vbo_data->m_data) != m_vbosActive.end()) {
m_currentVBO = m_vbosActive[vbo_data->m_data];
} else {
m_currentVBO = vbo_data;
m_vbosActive[vbo_data->m_data] = m_currentVBO;
}
m_currentVBO->bind(commandBuffer);
}
}
void KRMeshManager::startFrame(float deltaTime)
{
m_memoryTransferredThisFrame = 0;
if (m_draw_call_log_used) {
// Only log draw calls on the next frame if the draw call log was used on last frame
m_draw_call_log_used = false;
m_draw_call_logging_enabled = true;
}
m_draw_calls.clear();
// TODO - Implement proper double-buffering to reduce copy operations
m_streamerFenceMutex.lock();
if (m_streamerComplete) {
assert(m_activeVBOs_streamer_copy.size() == 0); // The streamer should have emptied this if it really did complete
const long KRENGINE_VBO_EXPIRY_FRAMES = 1;
std::set<KRVBOData*> expiredVBOs;
for (auto itr = m_vbosActive.begin(); itr != m_vbosActive.end(); itr++) {
KRVBOData* activeVBO = (*itr).second;
activeVBO->_swapHandles();
if (activeVBO->getType() == KRVBOData::CONSTANT) {
// Ensure that CONSTANT data is always loaded
float priority = std::numeric_limits<float>::max();
m_activeVBOs_streamer_copy.push_back(std::pair<float, KRVBOData*>(priority, activeVBO));
} else if (activeVBO->getLastFrameUsed() + KRENGINE_VBO_EXPIRY_FRAMES < getContext().getCurrentFrame()) {
// Expire VBO's that haven't been used in a long time
switch (activeVBO->getType()) {
case KRVBOData::STREAMING:
case KRVBOData::IMMEDIATE:
activeVBO->unload();
break;
case KRVBOData::CONSTANT:
// CONSTANT VBO's are not unloaded
break;
}
expiredVBOs.insert(activeVBO);
} else if (activeVBO->getType() == KRVBOData::STREAMING) {
float priority = activeVBO->getStreamPriority();
m_activeVBOs_streamer_copy.push_back(std::pair<float, KRVBOData*>(priority, activeVBO));
}
}
for (std::set<KRVBOData*>::iterator itr = expiredVBOs.begin(); itr != expiredVBOs.end(); itr++) {
m_vbosActive.erase((*itr)->m_data);
}
if (m_activeVBOs_streamer_copy.size() > 0) {
m_streamerComplete = false;
}
}
m_streamerFenceMutex.unlock();
}
void KRMeshManager::endFrame(float deltaTime)
{
m_currentVBO = nullptr;
}
void KRMeshManager::doStreaming(long& memoryRemaining, long& memoryRemainingThisFrame)
{
// TODO - Implement proper double-buffering to reduce copy operations
m_streamerFenceMutex.lock();
m_activeVBOs_streamer = std::move(m_activeVBOs_streamer_copy);
m_streamerFenceMutex.unlock();
if (m_activeVBOs_streamer.size() > 0) {
balanceVBOMemory(memoryRemaining, memoryRemainingThisFrame);
m_streamerFenceMutex.lock();
m_streamerComplete = true;
m_streamerFenceMutex.unlock();
} else {
memoryRemaining -= getMemUsed();
}
}
void KRMeshManager::balanceVBOMemory(long& memoryRemaining, long& memoryRemainingThisFrame)
{
std::sort(m_activeVBOs_streamer.begin(), m_activeVBOs_streamer.end(), std::greater<std::pair<float, KRVBOData*>>());
for (auto vbo_itr = m_activeVBOs_streamer.begin(); vbo_itr != m_activeVBOs_streamer.end(); vbo_itr++) {
KRVBOData* vbo_data = (*vbo_itr).second;
long vbo_size = vbo_data->getSize();
if (!vbo_data->isVBOLoaded()) {
if (memoryRemainingThisFrame > vbo_size) {
vbo_data->load();
memoryRemainingThisFrame -= vbo_size;
}
}
memoryRemaining -= vbo_size;
}
}
long KRMeshManager::getMemUsed()
{
return m_vboMemUsed;
}
long KRMeshManager::getMemActive()
{
long mem_active = 0;
for (unordered_map<Block*, KRVBOData*>::iterator itr = m_vbosActive.begin(); itr != m_vbosActive.end(); itr++) {
mem_active += (*itr).second->getSize();
}
return mem_active;
}
void KRMeshManager::initVolumetricLightingVertexes()
{
if (m_volumetricLightingVertexData.getSize() == 0) {
m_volumetricLightingVertexData.expand(sizeof(VolumetricLightingVertexData) * KRENGINE_MAX_VOLUMETRIC_PLANES * 6);
m_volumetricLightingVertexData.lock();
VolumetricLightingVertexData* vertex_data = (VolumetricLightingVertexData*)m_volumetricLightingVertexData.getStart();
int iVertex = 0;
for (int iPlane = 0; iPlane < KRENGINE_MAX_VOLUMETRIC_PLANES; iPlane++) {
vertex_data[iVertex].vertex.x = -1.0f;
vertex_data[iVertex].vertex.y = -1.0f;
vertex_data[iVertex].vertex.z = (float)iPlane;
iVertex++;
vertex_data[iVertex].vertex.x = 1.0f;
vertex_data[iVertex].vertex.y = -1.0f;
vertex_data[iVertex].vertex.z = (float)iPlane;
iVertex++;
vertex_data[iVertex].vertex.x = -1.0f;
vertex_data[iVertex].vertex.y = 1.0f;
vertex_data[iVertex].vertex.z = (float)iPlane;
iVertex++;
vertex_data[iVertex].vertex.x = -1.0f;
vertex_data[iVertex].vertex.y = 1.0f;
vertex_data[iVertex].vertex.z = (float)iPlane;
iVertex++;
vertex_data[iVertex].vertex.x = 1.0f;
vertex_data[iVertex].vertex.y = -1.0f;
vertex_data[iVertex].vertex.z = (float)iPlane;
iVertex++;
vertex_data[iVertex].vertex.x = 1.0f;
vertex_data[iVertex].vertex.y = 1.0f;
vertex_data[iVertex].vertex.z = (float)iPlane;
iVertex++;
}
KRENGINE_VBO_DATA_VOLUMETRIC_LIGHTING.init(this, &m_volumetricLightingVertexData, nullptr, (1 << KRMesh::KRENGINE_ATTRIB_VERTEX), false, KRVBOData::CONSTANT
#if KRENGINE_DEBUG_GPU_LABELS
, "Volumetric Lighting Planes [built-in]"
#endif
);
m_volumetricLightingVertexData.unlock();
}
}
void KRMeshManager::initRandomParticles()
{
if (m_randomParticleVertexData.getSize() == 0) {
m_randomParticleVertexData.expand(sizeof(RandomParticleVertexData) * KRENGINE_MAX_RANDOM_PARTICLES * 3);
m_randomParticleVertexData.lock();
RandomParticleVertexData* vertex_data = (RandomParticleVertexData*)m_randomParticleVertexData.getStart();
// Generate vertices for randomly placed equilateral triangles with a side length of 1 and an origin point centered so that an inscribed circle can be efficiently rendered without wasting fill
float equilateral_triangle_height = sqrt(3.0f) * 0.5f;
float inscribed_circle_radius = 1.0f / (2.0f * sqrt(3.0f));
int iVertex = 0;
for (int iParticle = 0; iParticle < KRENGINE_MAX_RANDOM_PARTICLES; iParticle++) {
vertex_data[iVertex].vertex.x = (float)(rand() % 2000) / 1000.0f - 1000.0f;
vertex_data[iVertex].vertex.y = (float)(rand() % 2000) / 1000.0f - 1000.0f;
vertex_data[iVertex].vertex.z = (float)(rand() % 2000) / 1000.0f - 1000.0f;
vertex_data[iVertex].uva.x = -0.5f;
vertex_data[iVertex].uva.y = -inscribed_circle_radius;
iVertex++;
vertex_data[iVertex].vertex.x = vertex_data[iVertex - 1].vertex.x;
vertex_data[iVertex].vertex.y = vertex_data[iVertex - 1].vertex.y;
vertex_data[iVertex].vertex.z = vertex_data[iVertex - 1].vertex.z;
vertex_data[iVertex].uva.x = 0.5f;
vertex_data[iVertex].uva.y = -inscribed_circle_radius;
iVertex++;
vertex_data[iVertex].vertex.x = vertex_data[iVertex - 1].vertex.x;
vertex_data[iVertex].vertex.y = vertex_data[iVertex - 1].vertex.y;
vertex_data[iVertex].vertex.z = vertex_data[iVertex - 1].vertex.z;
vertex_data[iVertex].uva.x = 0.0f;
vertex_data[iVertex].uva.y = -inscribed_circle_radius + equilateral_triangle_height;
iVertex++;
}
KRENGINE_VBO_DATA_RANDOM_PARTICLES.init(this, &m_randomParticleVertexData, nullptr, (1 << KRMesh::KRENGINE_ATTRIB_VERTEX) | (1 << KRMesh::KRENGINE_ATTRIB_TEXUVA), false, KRVBOData::CONSTANT
#if KRENGINE_DEBUG_GPU_LABELS
, "Random Particles [built-in]"
#endif
);
m_randomParticleVertexData.unlock();
}
}
long KRMeshManager::getMemoryTransferedThisFrame()
{
return m_memoryTransferredThisFrame;
}
size_t KRMeshManager::getActiveVBOCount()
{
return m_vbosActive.size();
}
void KRMeshManager::log_draw_call(RenderPassType pass, const std::string& object_name, const std::string& material_name, int vertex_count)
{
if (m_draw_call_logging_enabled) {
draw_call_info info;
info.pass = pass;
strncpy(info.object_name, object_name.c_str(), 256);
strncpy(info.material_name, material_name.c_str(), 256);
info.vertex_count = vertex_count;
m_draw_calls.push_back(info);
}
}
std::vector<KRMeshManager::draw_call_info> KRMeshManager::getDrawCalls()
{
m_draw_call_log_used = true;
return m_draw_calls;
}
KRMeshManager::KRVBOData::KRVBOData()
{
m_debugLabel[0] = '\0';
m_is_vbo_loaded = false;
m_is_vbo_ready = false;
m_manager = NULL;
m_type = STREAMING;
m_data = NULL;
m_index_data = NULL;
m_vertex_attrib_flags = 0;
m_size = 0;
m_last_frame_used = 0;
m_last_frame_max_lod_coverage = 0.0f;
memset(m_allocations, 0, sizeof(AllocationInfo) * KRENGINE_MAX_GPU_COUNT);
}
KRMeshManager::KRVBOData::KRVBOData(KRMeshManager* manager, Block* data, Block* index_data, int vertex_attrib_flags, bool static_vbo, vbo_type t
#if KRENGINE_DEBUG_GPU_LABELS
, const char* debug_label
#endif
)
{
m_debugLabel[0] = '\0';
memset(m_allocations, 0, sizeof(AllocationInfo) * KRENGINE_MAX_GPU_COUNT);
m_is_vbo_loaded = false;
m_is_vbo_ready = false;
init(manager, data, index_data, vertex_attrib_flags, static_vbo, t
#if KRENGINE_DEBUG_GPU_LABELS
, debug_label
#endif
);
}
void KRMeshManager::KRVBOData::init(KRMeshManager* manager, Block* data, Block* index_data, int vertex_attrib_flags, bool static_vbo, vbo_type t
#if KRENGINE_DEBUG_GPU_LABELS
, const char* debug_label
#endif
)
{
#if KRENGINE_DEBUG_GPU_LABELS
snprintf(m_debugLabel, KRENGINE_DEBUG_GPU_LABEL_MAX_LEN, debug_label);
#endif //KRENGINE_DEBUG_GPU_LABELS
m_manager = manager;
m_type = t;
m_static_vbo = static_vbo;
m_data = data;
m_index_data = index_data;
m_vertex_attrib_flags = vertex_attrib_flags;
m_size = m_data->getSize();
if (m_index_data != NULL) {
m_size += m_index_data->getSize();
}
if (t == KRVBOData::CONSTANT) {
m_manager->primeVBO(this);
}
}
KRMeshManager::KRVBOData::~KRVBOData()
{
// TODO - This needs to be done by the streamer thread, and asserted here...
unload();
}
void KRMeshManager::KRVBOData::load()
{
// TODO - This is a bit messy. Clean up after Vulkan refactor.
VkCommandBuffer noCommandBuffer = VK_NULL_HANDLE;
load(noCommandBuffer);
}
void KRMeshManager::KRVBOData::load(VkCommandBuffer& commandBuffer)
{
// TODO - We should load on each GPU only if there is a surface using the mesh
if (isVBOLoaded()) {
return;
}
KRDeviceManager* deviceManager = m_manager->getContext().getDeviceManager();
int iAllocation = 0;
for (auto deviceItr = deviceManager->getDevices().begin(); deviceItr != deviceManager->getDevices().end() && iAllocation < KRENGINE_MAX_GPU_COUNT; deviceItr++, iAllocation++) {
KRDevice& device = *(*deviceItr).second;
KrDeviceHandle deviceHandle = (*deviceItr).first;
VmaAllocator allocator = device.getAllocator();
AllocationInfo& allocation = m_allocations[iAllocation];
allocation.device = deviceHandle;
#if KRENGINE_DEBUG_GPU_LABELS
char debug_label[KRENGINE_DEBUG_GPU_LABEL_MAX_LEN];
const char* type_label = "";
switch (m_type) {
case vbo_type::STREAMING:
type_label = "Streaming";
break;
case vbo_type::CONSTANT:
type_label = "Constant";
break;
case vbo_type::IMMEDIATE:
type_label = "Immediate";
break;
default:
assert(false);
}
snprintf(debug_label, KRENGINE_DEBUG_GPU_LABEL_MAX_LEN, "%s Vertices: %s", type_label, m_debugLabel);
#endif // KRENGINE_DEBUG_GPU_LABELS
device.createBuffer(
m_data->getSize(),
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
&allocation.vertex_buffer,
&allocation.vertex_allocation
#if KRENGINE_DEBUG_GPU_LABELS
, debug_label
#endif // KRENGINE_DEBUG_GPU_LABELS
);
if (m_type == vbo_type::IMMEDIATE) {
device.graphicsUpload(commandBuffer, *m_data, allocation.vertex_buffer);
} else {
device.streamUpload(*m_data, allocation.vertex_buffer);
}
if (m_index_data && m_index_data->getSize() > 0) {
#if KRENGINE_DEBUG_GPU_LABELS
snprintf(debug_label, KRENGINE_DEBUG_GPU_LABEL_MAX_LEN, "%s Indexes: %s", type_label, m_debugLabel);
#endif // KRENGINE_DEBUG_GPU_LABELS
device.createBuffer(
m_index_data->getSize(),
VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
&allocation.index_buffer,
&allocation.index_allocation
#if KRENGINE_DEBUG_GPU_LABELS
, debug_label
#endif
);
if (m_type == vbo_type::IMMEDIATE) {
device.graphicsUpload(commandBuffer, *m_index_data, allocation.index_buffer);
} else {
device.streamUpload(*m_index_data, allocation.index_buffer);
}
}
}
m_is_vbo_loaded = true;
m_manager->m_vboMemUsed += getSize();
m_manager->m_memoryTransferredThisFrame += getSize();
if (m_type != STREAMING) {
_swapHandles();
}
}
void KRMeshManager::KRVBOData::unload()
{
KRDeviceManager* deviceManager = m_manager->getContext().getDeviceManager();
for (int i = 0; i < KRENGINE_MAX_GPU_COUNT; i++) {
AllocationInfo& allocation = m_allocations[i];
if (allocation.device) {
std::unique_ptr<KRDevice>& device = deviceManager->getDevice(allocation.device);
if (device) {
VmaAllocator allocator = device->getAllocator();
vmaDestroyBuffer(allocator, allocation.vertex_buffer, allocation.vertex_allocation);
if (allocation.index_buffer) {
vmaDestroyBuffer(allocator, allocation.index_buffer, allocation.index_allocation);
}
}
}
memset(&allocation, 0, sizeof(AllocationInfo));
}
if (isVBOLoaded()) {
m_manager->m_vboMemUsed -= getSize();
}
m_is_vbo_loaded = false;
m_is_vbo_ready = false;
}
void KRMeshManager::KRVBOData::bind(VkCommandBuffer& commandBuffer)
{
VkBuffer vertexBuffers[] = { getVertexBuffer() };
VkDeviceSize offsets[] = { 0 };
vkCmdBindVertexBuffers(commandBuffer, 0, 1, vertexBuffers, offsets);
if (m_index_data && m_index_data->getSize() > 0) {
// TODO - Support 32-bit index buffers
vkCmdBindIndexBuffer(commandBuffer, getIndexBuffer(), 0, VK_INDEX_TYPE_UINT16);
}
}
void KRMeshManager::KRVBOData::resetPoolExpiry(float lodCoverage)
{
long current_frame = m_manager->getContext().getCurrentFrame();
if (current_frame != m_last_frame_used) {
m_last_frame_used = current_frame;
m_last_frame_max_lod_coverage = 0.0f;
m_manager->primeVBO(this);
}
m_last_frame_max_lod_coverage = KRMAX(lodCoverage, m_last_frame_max_lod_coverage);
}
float KRMeshManager::KRVBOData::getStreamPriority()
{
long current_frame = m_manager->getContext().getCurrentFrame();
if (current_frame > m_last_frame_used + 5) {
return 1.0f - KRCLAMP((float)(current_frame - m_last_frame_used) / 60.0f, 0.0f, 1.0f);
} else {
return 10000.0f + m_last_frame_max_lod_coverage * 10.0f;
}
}
void KRMeshManager::KRVBOData::_swapHandles()
{
m_is_vbo_ready = m_is_vbo_loaded;
}
void KRMeshManager::primeVBO(KRVBOData* vbo_data)
{
if (m_vbosActive.find(vbo_data->m_data) == m_vbosActive.end()) {
m_vbosActive[vbo_data->m_data] = vbo_data;
}
}
VkBuffer& KRMeshManager::KRVBOData::getVertexBuffer()
{
assert(m_is_vbo_ready);
return m_allocations->vertex_buffer;
}
VkBuffer& KRMeshManager::KRVBOData::getIndexBuffer()
{
assert(m_is_vbo_ready);
return m_allocations->index_buffer;
}
uint32_t KRMeshManager::KRVBOData::getVertexAttributes()
{
return m_vertex_attrib_flags;
}

View File

@@ -0,0 +1,248 @@
//
// KRMeshManager.h
// Kraken Engine
//
// Copyright 2024 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.
//
#pragma once
#include "KREngine-common.h"
#include "resources/KRResourceManager.h"
#include "KRContextObject.h"
#include "block.h"
#include "KRNode.h"
class KRContext;
class KRMesh;
enum RenderPassType : uint8_t;
class KRMeshManager : public KRResourceManager
{
public:
static const int KRENGINE_MAX_VOLUMETRIC_PLANES = 500;
static const int KRENGINE_MAX_RANDOM_PARTICLES = 150000;
KRMeshManager(KRContext& context);
void init();
virtual ~KRMeshManager();
virtual KRResource* loadResource(const std::string& name, const std::string& extension, mimir::Block* data) override;
virtual KRResource* getResource(const std::string& name, const std::string& extension) override;
void startFrame(float deltaTime);
void endFrame(float deltaTime);
KRMesh* loadModel(const char* szName, mimir::Block* pData);
std::vector<KRMesh*> getModel(const char* szName);
KRMesh* getMaxLODModel(const char* szName);
void addModel(KRMesh* model);
std::vector<std::string> getModelNames();
unordered_multimap<std::string, KRMesh*>& getModels();
class KRVBOData
{
public:
typedef enum
{
STREAMING,
// STREAMING data is loaded asynchronously, with transfer queues in the streamer thread.
CONSTANT,
// CONSTANT data is loaded asyncrhronously, with transfer queues in the streamer thread, but is not unloaded.
IMMEDIATE
// IMMEDIATE data is loaded synchronously, with graphics queues in the presentation threads.
// IMMEDIATE data is available for use immediately on the same frame it is generated.
} vbo_type;
KRVBOData();
KRVBOData(KRMeshManager* manager, mimir::Block* data, mimir::Block* index_data, int vertex_attrib_flags, bool static_vbo, vbo_type t
#if KRENGINE_DEBUG_GPU_LABELS
, const char* debug_label
#endif
);
void init(KRMeshManager* manager, mimir::Block* data, mimir::Block* index_data, int vertex_attrib_flags, bool static_vbo, vbo_type t
#if KRENGINE_DEBUG_GPU_LABELS
, const char* debug_label
#endif
);
~KRVBOData();
mimir::Block* m_data;
mimir::Block* m_index_data;
bool isVBOLoaded()
{
return m_is_vbo_loaded;
}
bool isVBOReady() const
{
return m_is_vbo_ready;
}
void load();
void load(VkCommandBuffer& commandBuffer);
void unload();
void bind(VkCommandBuffer& commandBuffer);
// KRMeshManager depends on the address of KRVBOData's being constant
// after allocation. This is enforced by deleted copy constructors.
KRVBOData(const KRVBOData& o) = delete;
KRVBOData& operator=(const KRVBOData& o) = delete;
long getSize()
{
return (long)m_size;
}
void resetPoolExpiry(float lodCoverage);
long getLastFrameUsed()
{
return m_last_frame_used;
}
vbo_type getType()
{
return m_type;
}
float getStreamPriority();
void _swapHandles();
VkBuffer& getVertexBuffer();
VkBuffer& getIndexBuffer();
uint32_t getVertexAttributes();
private:
KRMeshManager* m_manager;
int m_vertex_attrib_flags;
long 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;
typedef struct
{
KrDeviceHandle device;
VkBuffer vertex_buffer;
VmaAllocation vertex_allocation;
VkBuffer index_buffer;
VmaAllocation index_allocation;
} AllocationInfo;
AllocationInfo m_allocations[KRENGINE_MAX_GPU_COUNT];
#if KRENGINE_DEBUG_GPU_LABELS
char m_debugLabel[KRENGINE_DEBUG_GPU_LABEL_MAX_LEN];
#endif
};
void bindVBO(VkCommandBuffer& commandBuffer, KRVBOData* vbo_data, float lodCoverage);
long getMemUsed();
long getMemActive();
typedef struct
{
hydra::Vector3 vertex;
hydra::Vector2 uva;
} RandomParticleVertexData;
typedef struct
{
hydra::Vector3 vertex;
} VolumetricLightingVertexData;
long getMemoryTransferedThisFrame();
size_t getActiveVBOCount();
struct draw_call_info
{
RenderPassType pass;
char object_name[256];
char material_name[256];
int vertex_count;
};
void log_draw_call(RenderPassType pass, const std::string& object_name, const std::string& material_name, int vertex_count);
std::vector<draw_call_info> getDrawCalls();
KRVBOData KRENGINE_VBO_DATA_3D_CUBE_VERTICES;
KRVBOData KRENGINE_VBO_DATA_2D_SQUARE_VERTICES;
KRVBOData KRENGINE_VBO_DATA_RANDOM_PARTICLES;
KRVBOData KRENGINE_VBO_DATA_VOLUMETRIC_LIGHTING;
void doStreaming(long& memoryRemaining, long& memoryRemainingThisFrame);
private:
mimir::Block KRENGINE_VBO_3D_CUBE_VERTICES;
__int32_t KRENGINE_VBO_3D_CUBE_ATTRIBS;
mimir::Block KRENGINE_VBO_2D_SQUARE_VERTICES;
__int32_t KRENGINE_VBO_2D_SQUARE_ATTRIBS;
unordered_multimap<std::string, KRMesh*> 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<mimir::Block*, KRVBOData*> m_vbosActive;
std::vector<std::pair<float, KRVBOData*> > m_activeVBOs_streamer;
std::vector<std::pair<float, KRVBOData*> > m_activeVBOs_streamer_copy;
mimir::Block m_randomParticleVertexData;
mimir::Block m_volumetricLightingVertexData;
long m_memoryTransferredThisFrame;
std::vector<draw_call_info> m_draw_calls;
bool m_draw_call_logging_enabled;
bool m_draw_call_log_used;
std::mutex m_streamerFenceMutex;
bool m_streamerComplete;
void balanceVBOMemory(long& memoryRemaining, long& memoryRemainingThisFrame);
void primeVBO(KRVBOData* vbo_data);
void initRandomParticles();
void initVolumetricLightingVertexes();
};

View File

@@ -0,0 +1,64 @@
//
// KRMeshQuad.cpp
// Kraken Engine
//
// Copyright 2024 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"
using namespace hydra;
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((int)mi.vertices.size());
mi.material_names.push_back("");
mi.format = ModelFormat::KRENGINE_MODEL_FORMAT_STRIP;
LoadData(mi, true, true);
}
KRMeshQuad::~KRMeshQuad()
{
}

View File

@@ -0,0 +1,42 @@
//
// KRMeshQuad.h
// Kraken Engine
//
// Copyright 2024 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.
//
#pragma once
#include "KRMesh.h"
class KRMeshQuad : public KRMesh
{
public:
KRMeshQuad(KRContext& context);
virtual ~KRMeshQuad();
private:
};

View File

@@ -0,0 +1,129 @@
//
// KRMeshSphere.cpp
// Kraken Engine
//
// Copyright 2024 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"
using namespace hydra;
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 = (int)(pow(4, iterations) * 8.0f);
std::vector<Triangle3> f = std::vector<Triangle3>(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][0] = p[0]; f[0][1] = p[3]; f[0][2] = p[4];
f[1][0] = p[0]; f[1][1] = p[4]; f[1][2] = p[5];
f[2][0] = p[0]; f[2][1] = p[5]; f[2][2] = p[2];
f[3][0] = p[0]; f[3][1] = p[2]; f[3][2] = p[3];
f[4][0] = p[1]; f[4][1] = p[4]; f[4][2] = p[3];
f[5][0] = p[1]; f[5][1] = p[5]; f[5][2] = p[4];
f[6][0] = p[1]; f[6][1] = p[2]; f[6][2] = p[5];
f[7][0] = p[1]; f[7][1] = p[3]; f[7][2] = p[2];
nt = 8;
/* Bisect each edge and move to the surface of a unit sphere */
for (it = 0; it < iterations; it++) {
ntold = nt;
for (i = 0; i < ntold; i++) {
pa.x = (f[i][0].x + f[i][1].x) / 2;
pa.y = (f[i][0].y + f[i][1].y) / 2;
pa.z = (f[i][0].z + f[i][1].z) / 2;
pb.x = (f[i][1].x + f[i][2].x) / 2;
pb.y = (f[i][1].y + f[i][2].y) / 2;
pb.z = (f[i][1].z + f[i][2].z) / 2;
pc.x = (f[i][2].x + f[i][0].x) / 2;
pc.y = (f[i][2].y + f[i][0].y) / 2;
pc.z = (f[i][2].z + f[i][0].z) / 2;
pa.normalize();
pb.normalize();
pc.normalize();
f[nt][0] = f[i][0]; f[nt][1] = pa; f[nt][2] = pc; nt++;
f[nt][0] = pa; f[nt][1] = f[i][1]; f[nt][2] = pb; nt++;
f[nt][0] = pb; f[nt][1] = f[i][2]; f[nt][2] = pc; nt++;
f[i][0] = pa;
f[i][1] = pb;
f[i][2] = pc;
}
}
for (int facet_index = 0; facet_index < facet_count; facet_index++) {
mi.vertices.push_back(f[facet_index][0]);
mi.vertices.push_back(f[facet_index][1]);
mi.vertices.push_back(f[facet_index][2]);
}
mi.submesh_starts.push_back(0);
mi.submesh_lengths.push_back((int)mi.vertices.size());
mi.material_names.push_back("");
mi.format = ModelFormat::KRENGINE_MODEL_FORMAT_TRIANGLES;
// Generate normals pointing away from center of sphere.
for (int vertex_index = 0; vertex_index < mi.vertices.size(); vertex_index++) {
mi.normals.push_back(Vector3::Normalize(mi.vertices[vertex_index] - Vector3::Zero()));
}
LoadData(mi, true, true);
}
KRMeshSphere::~KRMeshSphere()
{
}

View File

@@ -0,0 +1,42 @@
//
// KRMeshSphere.h
// Kraken Engine
//
// Copyright 2024 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.
//
#pragma once
#include "KRMesh.h"
class KRMeshSphere : public KRMesh
{
public:
KRMeshSphere(KRContext& context);
virtual ~KRMeshSphere();
private:
};

View File

@@ -0,0 +1,67 @@
//
// KRUnknown.cpp
// Kraken Engine
//
// Copyright 2024 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 "KRUnknown.h"
using namespace mimir;
KRUnknown::KRUnknown(KRContext& context, std::string name, std::string extension) : KRResource(context, name)
{
m_pData = new Block();
m_extension = extension;
}
KRUnknown::KRUnknown(KRContext& context, std::string name, std::string extension, Block* data) : KRResource(context, name)
{
m_pData = data;
m_extension = extension;
}
KRUnknown::~KRUnknown()
{
delete m_pData;
}
std::string KRUnknown::getExtension()
{
return m_extension;
}
bool KRUnknown::save(Block& data)
{
data.append(*m_pData);
return true;
}
Block* KRUnknown::getData()
{
return m_pData;
}

View File

@@ -0,0 +1,57 @@
//
// KRUnknown.h
// Kraken Engine
//
// Copyright 2024 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.
//
#pragma once
#include "KREngine-common.h"
#include "KRContextObject.h"
#include "block.h"
#include "resources/KRResource.h"
class KRUnknown : public KRResource
{
public:
KRUnknown(KRContext& context, std::string name, std::string extension);
KRUnknown(KRContext& context, std::string name, std::string extension, mimir::Block* data);
virtual ~KRUnknown();
virtual std::string getExtension();
virtual bool save(mimir::Block& data);
mimir::Block* getData();
private:
std::string m_extension;
mimir::Block* m_pData;
};

View File

@@ -0,0 +1,117 @@
//
// KRUnknownManager.cpp
// Kraken Engine
//
// Copyright 2024 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 "KRUnknownManager.h"
#include "KREngine-common.h"
using namespace mimir;
KRUnknownManager::KRUnknownManager(KRContext& context) : KRResourceManager(context)
{
}
KRUnknownManager::~KRUnknownManager()
{
for (unordered_map<std::string, unordered_map<std::string, KRUnknown*> >::iterator extension_itr = m_unknowns.begin(); extension_itr != m_unknowns.end(); extension_itr++) {
for (unordered_map<std::string, KRUnknown*>::iterator name_itr = (*extension_itr).second.begin(); name_itr != (*extension_itr).second.end(); name_itr++) {
delete (*name_itr).second;
}
}
}
unordered_map<std::string, unordered_map<std::string, KRUnknown*> >& KRUnknownManager::getUnknowns()
{
return m_unknowns;
}
void KRUnknownManager::add(KRUnknown* unknown)
{
std::string lower_name = unknown->getName();
std::string lower_extension = unknown->getExtension();
std::transform(lower_name.begin(), lower_name.end(), lower_name.begin(), ::tolower);
std::transform(lower_extension.begin(), lower_extension.end(), lower_extension.begin(), ::tolower);
unordered_map<std::string, unordered_map<std::string, KRUnknown*> >::iterator extension_itr = m_unknowns.find(lower_extension);
if (extension_itr == m_unknowns.end()) {
m_unknowns[lower_extension] = unordered_map<std::string, KRUnknown*>();
extension_itr = m_unknowns.find(lower_extension);
}
unordered_map<std::string, KRUnknown*>::iterator name_itr = (*extension_itr).second.find(lower_name);
if (name_itr != (*extension_itr).second.end()) {
delete (*name_itr).second;
(*name_itr).second = unknown;
} else {
(*extension_itr).second[lower_name] = unknown;
}
}
KRResource* KRUnknownManager::loadResource(const std::string& name, const std::string& extension, Block* data)
{
// KRUnknown's can have any extension
return load(name, extension, data);
}
KRResource* KRUnknownManager::getResource(const std::string& name, const std::string& extension)
{
// KRUnknown's can have any extension
return get(name, extension);
}
KRUnknown* KRUnknownManager::load(const std::string& name, const std::string& extension, Block* data)
{
KRUnknown* unknown = new KRUnknown(getContext(), name, extension, data);
if (unknown) add(unknown);
return unknown;
}
KRUnknown* KRUnknownManager::get(const std::string& name, const std::string& extension)
{
std::string lower_name = name;
std::string lower_extension = extension;
std::transform(lower_name.begin(), lower_name.end(), lower_name.begin(), ::tolower);
std::transform(lower_extension.begin(), lower_extension.end(), lower_extension.begin(), ::tolower);
return m_unknowns[lower_extension][lower_name];
}
const unordered_map<std::string, KRUnknown*>& KRUnknownManager::get(const std::string& extension)
{
std::string lower_extension = extension;
std::transform(lower_extension.begin(), lower_extension.end(), lower_extension.begin(), ::tolower);
return m_unknowns[lower_extension];
}

View File

@@ -0,0 +1,63 @@
//
// KRUnknownManager.h
// Kraken Engine
//
// Copyright 2024 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.
//
#pragma once
#include "KREngine-common.h"
#include "resources/KRResourceManager.h"
#include "KRUnknown.h"
#include "KRContextObject.h"
#include "block.h"
class KRUnknownManager : public KRResourceManager
{
public:
KRUnknownManager(KRContext& context);
virtual ~KRUnknownManager();
virtual KRResource* loadResource(const std::string& name, const std::string& extension, mimir::Block* data) override;
virtual KRResource* getResource(const std::string& name, const std::string& extension) override;
void add(KRUnknown* unknown);
KRUnknown* load(const std::string& name, const std::string& extension, mimir::Block* data);
KRUnknown* get(const std::string& name, const std::string& extension);
const unordered_map<std::string, KRUnknown*>& get(const std::string& extension);
unordered_map<std::string, unordered_map<std::string, KRUnknown*> >& getUnknowns();
private:
unordered_map<std::string, unordered_map<std::string, KRUnknown*> > m_unknowns;
};