diff options
Diffstat (limited to 'drivers')
56 files changed, 9137 insertions, 3180 deletions
diff --git a/drivers/SCsub b/drivers/SCsub index 2c5e9434e8..f9cfa3fb05 100644 --- a/drivers/SCsub +++ b/drivers/SCsub @@ -21,6 +21,11 @@ if (env["platform"] == "windows"): if env['xaudio2']: SConscript("xaudio2/SCsub") +# Midi drivers +SConscript('alsamidi/SCsub') +SConscript('coremidi/SCsub') +SConscript('winmidi/SCsub') + # Graphics drivers if (env["platform"] != "server"): SConscript('gles3/SCsub') diff --git a/drivers/alsa/audio_driver_alsa.cpp b/drivers/alsa/audio_driver_alsa.cpp index 1e17e72532..a44a11a46d 100644 --- a/drivers/alsa/audio_driver_alsa.cpp +++ b/drivers/alsa/audio_driver_alsa.cpp @@ -58,7 +58,10 @@ Error AudioDriverALSA::init_device() { #define CHECK_FAIL(m_cond) \ if (m_cond) { \ fprintf(stderr, "ALSA ERR: %s\n", snd_strerror(status)); \ - snd_pcm_close(pcm_handle); \ + if (pcm_handle) { \ + snd_pcm_close(pcm_handle); \ + pcm_handle = NULL; \ + } \ ERR_FAIL_COND_V(m_cond, ERR_CANT_OPEN); \ } @@ -142,8 +145,6 @@ Error AudioDriverALSA::init_device() { samples_in.resize(period_size * channels); samples_out.resize(period_size * channels); - snd_pcm_nonblock(pcm_handle, 0); - return OK; } @@ -152,7 +153,6 @@ Error AudioDriverALSA::init() { active = false; thread_exited = false; exit_thread = false; - pcm_open = false; Error err = init_device(); if (err == OK) { @@ -168,54 +168,50 @@ void AudioDriverALSA::thread_func(void *p_udata) { AudioDriverALSA *ad = (AudioDriverALSA *)p_udata; while (!ad->exit_thread) { + + ad->lock(); + ad->start_counting_ticks(); + if (!ad->active) { for (unsigned int i = 0; i < ad->period_size * ad->channels; i++) { - ad->samples_out[i] = 0; - }; - } else { - ad->lock(); + ad->samples_out.write[i] = 0; + } + } else { ad->audio_server_process(ad->period_size, ad->samples_in.ptrw()); - ad->unlock(); - for (unsigned int i = 0; i < ad->period_size * ad->channels; i++) { - ad->samples_out[i] = ad->samples_in[i] >> 16; + ad->samples_out.write[i] = ad->samples_in[i] >> 16; } - }; + } int todo = ad->period_size; int total = 0; - while (todo) { - if (ad->exit_thread) - break; + while (todo && !ad->exit_thread) { uint8_t *src = (uint8_t *)ad->samples_out.ptr(); int wrote = snd_pcm_writei(ad->pcm_handle, (void *)(src + (total * ad->channels)), todo); - if (wrote < 0) { - if (ad->exit_thread) - break; + if (wrote > 0) { + total += wrote; + todo -= wrote; + } else if (wrote == -EAGAIN) { + ad->stop_counting_ticks(); + ad->unlock(); - if (wrote == -EAGAIN) { - //can't write yet (though this is blocking..) - usleep(1000); - continue; - } + OS::get_singleton()->delay_usec(1000); + + ad->lock(); + ad->start_counting_ticks(); + } else { wrote = snd_pcm_recover(ad->pcm_handle, wrote, 0); if (wrote < 0) { - //absolute fail - fprintf(stderr, "ALSA failed and can't recover: %s\n", snd_strerror(wrote)); + ERR_PRINTS("ALSA: Failed and can't recover: " + String(snd_strerror(wrote))); ad->active = false; ad->exit_thread = true; - break; } - continue; - }; - - total += wrote; - todo -= wrote; - }; + } + } // User selected a new device, finish the current one so we'll init the new device if (ad->device_name != ad->new_device) { @@ -232,10 +228,12 @@ void AudioDriverALSA::thread_func(void *p_udata) { if (err != OK) { ad->active = false; ad->exit_thread = true; - break; } } } + + ad->stop_counting_ticks(); + ad->unlock(); }; ad->thread_exited = true; @@ -296,7 +294,9 @@ String AudioDriverALSA::get_device() { void AudioDriverALSA::set_device(String device) { + lock(); new_device = device; + unlock(); } void AudioDriverALSA::lock() { @@ -315,29 +315,29 @@ void AudioDriverALSA::unlock() { void AudioDriverALSA::finish_device() { - if (pcm_open) { + if (pcm_handle) { snd_pcm_close(pcm_handle); - pcm_open = NULL; + pcm_handle = NULL; } } void AudioDriverALSA::finish() { - if (!thread) - return; - - exit_thread = true; - Thread::wait_to_finish(thread); + if (thread) { + exit_thread = true; + Thread::wait_to_finish(thread); - finish_device(); + memdelete(thread); + thread = NULL; - memdelete(thread); - if (mutex) { - memdelete(mutex); - mutex = NULL; + if (mutex) { + memdelete(mutex); + mutex = NULL; + } } - thread = NULL; -}; + + finish_device(); +} AudioDriverALSA::AudioDriverALSA() { diff --git a/drivers/alsa/audio_driver_alsa.h b/drivers/alsa/audio_driver_alsa.h index 2878e100a2..e2a2325cf3 100644 --- a/drivers/alsa/audio_driver_alsa.h +++ b/drivers/alsa/audio_driver_alsa.h @@ -66,7 +66,6 @@ class AudioDriverALSA : public AudioDriver { bool active; bool thread_exited; mutable bool exit_thread; - bool pcm_open; public: const char *get_name() const { diff --git a/drivers/alsamidi/SCsub b/drivers/alsamidi/SCsub new file mode 100644 index 0000000000..233593b0f9 --- /dev/null +++ b/drivers/alsamidi/SCsub @@ -0,0 +1,8 @@ +#!/usr/bin/env python + +Import('env') + +# Driver source files +env.add_source_files(env.drivers_sources, "*.cpp") + +Export('env') diff --git a/drivers/alsamidi/alsa_midi.cpp b/drivers/alsamidi/alsa_midi.cpp new file mode 100644 index 0000000000..599470d7e0 --- /dev/null +++ b/drivers/alsamidi/alsa_midi.cpp @@ -0,0 +1,201 @@ +/*************************************************************************/ +/* alsa_midi.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifdef ALSAMIDI_ENABLED + +#include <errno.h> + +#include "alsa_midi.h" +#include "os/os.h" +#include "print_string.h" + +static int get_message_size(uint8_t message) { + switch (message & 0xF0) { + case 0x80: // note off + case 0x90: // note on + case 0xA0: // aftertouch + case 0xB0: // continuous controller + return 3; + + case 0xC0: // patch change + case 0xD0: // channel pressure + case 0xE0: // pitch bend + return 2; + } + + return 256; +} + +void MIDIDriverALSAMidi::thread_func(void *p_udata) { + MIDIDriverALSAMidi *md = (MIDIDriverALSAMidi *)p_udata; + uint64_t timestamp = 0; + uint8_t buffer[256]; + int expected_size = 255; + int bytes = 0; + + while (!md->exit_thread) { + int ret; + + md->lock(); + + for (int i = 0; i < md->connected_inputs.size(); i++) { + snd_rawmidi_t *midi_in = md->connected_inputs[i]; + do { + uint8_t byte = 0; + ret = snd_rawmidi_read(midi_in, &byte, 1); + if (ret < 0) { + if (ret != -EAGAIN) { + ERR_PRINTS("snd_rawmidi_read error: " + String(snd_strerror(ret))); + } + } else { + if (byte & 0x80) { + // Flush previous packet if there is any + if (bytes) { + md->receive_input_packet(timestamp, buffer, bytes); + bytes = 0; + } + expected_size = get_message_size(byte); + } + + if (bytes < 256) { + buffer[bytes++] = byte; + // If we know the size of the current packet receive it if it reached the expected size + if (bytes >= expected_size) { + md->receive_input_packet(timestamp, buffer, bytes); + bytes = 0; + } + } + } + } while (ret > 0); + } + + md->unlock(); + + OS::get_singleton()->delay_usec(1000); + } +} + +Error MIDIDriverALSAMidi::open() { + + void **hints; + + if (snd_device_name_hint(-1, "rawmidi", &hints) < 0) + return ERR_CANT_OPEN; + + int i = 0; + for (void **n = hints; *n != NULL; n++) { + char *name = snd_device_name_get_hint(*n, "NAME"); + + if (name != NULL) { + snd_rawmidi_t *midi_in; + int ret = snd_rawmidi_open(&midi_in, NULL, name, SND_RAWMIDI_NONBLOCK); + if (ret >= 0) { + connected_inputs.insert(i++, midi_in); + } + } + + if (name != NULL) + free(name); + } + snd_device_name_free_hint(hints); + + mutex = Mutex::create(); + thread = Thread::create(MIDIDriverALSAMidi::thread_func, this); + + return OK; +} + +void MIDIDriverALSAMidi::close() { + + if (thread) { + exit_thread = true; + Thread::wait_to_finish(thread); + + memdelete(thread); + thread = NULL; + } + + if (mutex) { + memdelete(mutex); + mutex = NULL; + } + + for (int i = 0; i < connected_inputs.size(); i++) { + snd_rawmidi_t *midi_in = connected_inputs[i]; + snd_rawmidi_close(midi_in); + } + connected_inputs.clear(); +} + +void MIDIDriverALSAMidi::lock() const { + + if (mutex) + mutex->lock(); +} + +void MIDIDriverALSAMidi::unlock() const { + + if (mutex) + mutex->unlock(); +} + +PoolStringArray MIDIDriverALSAMidi::get_connected_inputs() { + + PoolStringArray list; + + lock(); + for (int i = 0; i < connected_inputs.size(); i++) { + snd_rawmidi_t *midi_in = connected_inputs[i]; + snd_rawmidi_info_t *info; + + snd_rawmidi_info_malloc(&info); + snd_rawmidi_info(midi_in, info); + list.push_back(snd_rawmidi_info_get_name(info)); + snd_rawmidi_info_free(info); + } + unlock(); + + return list; +} + +MIDIDriverALSAMidi::MIDIDriverALSAMidi() { + + mutex = NULL; + thread = NULL; + + exit_thread = false; +} + +MIDIDriverALSAMidi::~MIDIDriverALSAMidi() { + + close(); +} + +#endif diff --git a/drivers/alsamidi/alsa_midi.h b/drivers/alsamidi/alsa_midi.h new file mode 100644 index 0000000000..90e458a365 --- /dev/null +++ b/drivers/alsamidi/alsa_midi.h @@ -0,0 +1,69 @@ +/*************************************************************************/ +/* alsa_midi.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifdef ALSAMIDI_ENABLED + +#ifndef ALSA_MIDI_H +#define ALSA_MIDI_H + +#include <alsa/asoundlib.h> +#include <stdio.h> + +#include "core/os/mutex.h" +#include "core/os/thread.h" +#include "core/vector.h" +#include "os/midi_driver.h" + +class MIDIDriverALSAMidi : public MIDIDriver { + + Thread *thread; + Mutex *mutex; + + Vector<snd_rawmidi_t *> connected_inputs; + + bool exit_thread; + + static void thread_func(void *p_udata); + + void lock() const; + void unlock() const; + +public: + virtual Error open(); + virtual void close(); + + virtual PoolStringArray get_connected_inputs(); + + MIDIDriverALSAMidi(); + virtual ~MIDIDriverALSAMidi(); +}; + +#endif +#endif diff --git a/drivers/convex_decomp/b2d_decompose.h b/drivers/convex_decomp/b2d_decompose.h index 068689d73d..b21792047e 100644 --- a/drivers/convex_decomp/b2d_decompose.h +++ b/drivers/convex_decomp/b2d_decompose.h @@ -31,8 +31,8 @@ #ifndef B2D_DECOMPOSE_H #define B2D_DECOMPOSE_H -#include "math_2d.h" #include "vector.h" +#include "vector2.h" Vector<Vector<Vector2> > b2d_decompose(const Vector<Vector2> &p_polygon); #endif // B2D_DECOMPOSE_H diff --git a/drivers/coreaudio/audio_driver_coreaudio.cpp b/drivers/coreaudio/audio_driver_coreaudio.cpp index ef7858b4ca..e1f47cb8c2 100644 --- a/drivers/coreaudio/audio_driver_coreaudio.cpp +++ b/drivers/coreaudio/audio_driver_coreaudio.cpp @@ -35,8 +35,23 @@ #include "os/os.h" #define kOutputBus 0 +#define kInputBus 1 #ifdef OSX_ENABLED +OSStatus AudioDriverCoreAudio::input_device_address_cb(AudioObjectID inObjectID, + UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses, + void *inClientData) { + AudioDriverCoreAudio *driver = (AudioDriverCoreAudio *)inClientData; + + // If our selected device is the Default call set_device to update the + // kAudioOutputUnitProperty_CurrentDevice property + if (driver->capture_device_name == "Default") { + driver->capture_set_device("Default"); + } + + return noErr; +} + OSStatus AudioDriverCoreAudio::output_device_address_cb(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses, void *inClientData) { @@ -79,6 +94,11 @@ Error AudioDriverCoreAudio::init() { result = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &prop, &output_device_address_cb, this); ERR_FAIL_COND_V(result != noErr, FAILED); + + prop.mSelector = kAudioHardwarePropertyDefaultInputDevice; + + result = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &prop, &input_device_address_cb, this); + ERR_FAIL_COND_V(result != noErr, FAILED); #endif AudioStreamBasicDescription strdesc; @@ -102,7 +122,27 @@ Error AudioDriverCoreAudio::init() { break; } - mix_rate = GLOBAL_DEF("audio/mix_rate", DEFAULT_MIX_RATE); + zeromem(&strdesc, sizeof(strdesc)); + size = sizeof(strdesc); + result = AudioUnitGetProperty(audio_unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, kInputBus, &strdesc, &size); + ERR_FAIL_COND_V(result != noErr, FAILED); + + switch (strdesc.mChannelsPerFrame) { + case 1: // Mono + capture_channels = 1; + break; + + case 2: // Stereo + capture_channels = 2; + break; + + default: + // Unknown number of channels, default to stereo + capture_channels = 2; + break; + } + + mix_rate = GLOBAL_DEF_RST("audio/mix_rate", DEFAULT_MIX_RATE); zeromem(&strdesc, sizeof(strdesc)); strdesc.mFormatID = kAudioFormatLinearPCM; @@ -117,7 +157,12 @@ Error AudioDriverCoreAudio::init() { result = AudioUnitSetProperty(audio_unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, kOutputBus, &strdesc, sizeof(strdesc)); ERR_FAIL_COND_V(result != noErr, FAILED); - int latency = GLOBAL_DEF("audio/output_latency", DEFAULT_OUTPUT_LATENCY); + strdesc.mChannelsPerFrame = capture_channels; + + result = AudioUnitSetProperty(audio_unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, kInputBus, &strdesc, sizeof(strdesc)); + ERR_FAIL_COND_V(result != noErr, FAILED); + + int latency = GLOBAL_DEF_RST("audio/output_latency", DEFAULT_OUTPUT_LATENCY); // Sample rate is independent of channels (ref: https://stackoverflow.com/questions/11048825/audio-sample-frequency-rely-on-channels) buffer_frames = closest_power_of_2(latency * mix_rate / 1000); @@ -126,8 +171,12 @@ Error AudioDriverCoreAudio::init() { ERR_FAIL_COND_V(result != noErr, FAILED); #endif - buffer_size = buffer_frames * channels; + unsigned int buffer_size = buffer_frames * channels; samples_in.resize(buffer_size); + input_buf.resize(buffer_size); + input_buffer.resize(buffer_size * 8); + input_position = 0; + input_size = 0; if (OS::get_singleton()->is_stdout_verbose()) { print_line("CoreAudio: detected " + itos(channels) + " channels"); @@ -141,6 +190,12 @@ Error AudioDriverCoreAudio::init() { result = AudioUnitSetProperty(audio_unit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, kOutputBus, &callback, sizeof(callback)); ERR_FAIL_COND_V(result != noErr, FAILED); + zeromem(&callback, sizeof(AURenderCallbackStruct)); + callback.inputProc = &AudioDriverCoreAudio::input_callback; + callback.inputProcRefCon = this; + result = AudioUnitSetProperty(audio_unit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &callback, sizeof(callback)); + ERR_FAIL_COND_V(result != noErr, FAILED); + result = AudioUnitInitialize(audio_unit); ERR_FAIL_COND_V(result != noErr, FAILED); @@ -163,6 +218,8 @@ OSStatus AudioDriverCoreAudio::output_callback(void *inRefCon, return 0; }; + ad->start_counting_ticks(); + for (unsigned int i = 0; i < ioData->mNumberBuffers; i++) { AudioBuffer *abuf = &ioData->mBuffers[i]; @@ -184,11 +241,51 @@ OSStatus AudioDriverCoreAudio::output_callback(void *inRefCon, }; }; + ad->stop_counting_ticks(); ad->unlock(); return 0; }; +OSStatus AudioDriverCoreAudio::input_callback(void *inRefCon, + AudioUnitRenderActionFlags *ioActionFlags, + const AudioTimeStamp *inTimeStamp, + UInt32 inBusNumber, UInt32 inNumberFrames, + AudioBufferList *ioData) { + + AudioDriverCoreAudio *ad = (AudioDriverCoreAudio *)inRefCon; + if (!ad->active) { + return 0; + } + + ad->lock(); + + AudioBufferList bufferList; + bufferList.mNumberBuffers = 1; + bufferList.mBuffers[0].mData = ad->input_buf.ptrw(); + bufferList.mBuffers[0].mNumberChannels = ad->capture_channels; + bufferList.mBuffers[0].mDataByteSize = ad->input_buf.size() * sizeof(int16_t); + + OSStatus result = AudioUnitRender(ad->audio_unit, ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, &bufferList); + if (result == noErr) { + for (int i = 0; i < inNumberFrames * ad->capture_channels; i++) { + int32_t sample = ad->input_buf[i] << 16; + ad->input_buffer_write(sample); + + if (ad->capture_channels == 1) { + // In case input device is single channel convert it to Stereo + ad->input_buffer_write(sample); + } + } + } else { + ERR_PRINT(("AudioUnitRender failed, code: " + itos(result)).utf8().get_data()); + } + + ad->unlock(); + + return result; +} + void AudioDriverCoreAudio::start() { if (!active) { OSStatus result = AudioOutputUnitStart(audio_unit); @@ -219,9 +316,94 @@ AudioDriver::SpeakerMode AudioDriverCoreAudio::get_speaker_mode() const { return get_speaker_mode_by_total_channels(channels); }; +void AudioDriverCoreAudio::lock() { + if (mutex) + mutex->lock(); +}; + +void AudioDriverCoreAudio::unlock() { + if (mutex) + mutex->unlock(); +}; + +bool AudioDriverCoreAudio::try_lock() { + if (mutex) + return mutex->try_lock() == OK; + return true; +} + +void AudioDriverCoreAudio::finish() { + OSStatus result; + + lock(); + + AURenderCallbackStruct callback; + zeromem(&callback, sizeof(AURenderCallbackStruct)); + result = AudioUnitSetProperty(audio_unit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, kOutputBus, &callback, sizeof(callback)); + if (result != noErr) { + ERR_PRINT("AudioUnitSetProperty failed"); + } + + if (active) { + result = AudioOutputUnitStop(audio_unit); + if (result != noErr) { + ERR_PRINT("AudioOutputUnitStop failed"); + } + + active = false; + } + + result = AudioUnitUninitialize(audio_unit); + if (result != noErr) { + ERR_PRINT("AudioUnitUninitialize failed"); + } + #ifdef OSX_ENABLED + AudioObjectPropertyAddress prop; + prop.mSelector = kAudioHardwarePropertyDefaultOutputDevice; + prop.mScope = kAudioObjectPropertyScopeGlobal; + prop.mElement = kAudioObjectPropertyElementMaster; -Array AudioDriverCoreAudio::get_device_list() { + result = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &prop, &output_device_address_cb, this); + if (result != noErr) { + ERR_PRINT("AudioObjectRemovePropertyListener failed"); + } +#endif + + result = AudioComponentInstanceDispose(audio_unit); + if (result != noErr) { + ERR_PRINT("AudioComponentInstanceDispose failed"); + } + + unlock(); + + if (mutex) { + memdelete(mutex); + mutex = NULL; + } +}; + +Error AudioDriverCoreAudio::capture_start() { + + UInt32 flag = 1; + OSStatus result = AudioUnitSetProperty(audio_unit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, kInputBus, &flag, sizeof(flag)); + ERR_FAIL_COND_V(result != noErr, FAILED); + + return OK; +} + +Error AudioDriverCoreAudio::capture_stop() { + + UInt32 flag = 0; + OSStatus result = AudioUnitSetProperty(audio_unit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, kInputBus, &flag, sizeof(flag)); + ERR_FAIL_COND_V(result != noErr, FAILED); + + return OK; +} + +#ifdef OSX_ENABLED + +Array AudioDriverCoreAudio::_get_device_list(bool capture) { Array list; @@ -240,20 +422,20 @@ Array AudioDriverCoreAudio::get_device_list() { UInt32 deviceCount = size / sizeof(AudioDeviceID); for (UInt32 i = 0; i < deviceCount; i++) { - prop.mScope = kAudioDevicePropertyScopeOutput; + prop.mScope = capture ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput; prop.mSelector = kAudioDevicePropertyStreamConfiguration; AudioObjectGetPropertyDataSize(audioDevices[i], &prop, 0, NULL, &size); AudioBufferList *bufferList = (AudioBufferList *)malloc(size); AudioObjectGetPropertyData(audioDevices[i], &prop, 0, NULL, &size, bufferList); - UInt32 outputChannelCount = 0; + UInt32 channelCount = 0; for (UInt32 j = 0; j < bufferList->mNumberBuffers; j++) - outputChannelCount += bufferList->mBuffers[j].mNumberChannels; + channelCount += bufferList->mBuffers[j].mNumberChannels; free(bufferList); - if (outputChannelCount >= 1) { + if (channelCount >= 1) { CFStringRef cfname; size = sizeof(CFStringRef); @@ -278,21 +460,11 @@ Array AudioDriverCoreAudio::get_device_list() { return list; } -String AudioDriverCoreAudio::get_device() { - - return device_name; -} - -void AudioDriverCoreAudio::set_device(String device) { - - device_name = device; - if (!active) { - return; - } +void AudioDriverCoreAudio::_set_device(const String &device, bool capture) { AudioDeviceID deviceId; bool found = false; - if (device_name != "Default") { + if (device != "Default") { AudioObjectPropertyAddress prop; prop.mSelector = kAudioHardwarePropertyDevices; @@ -306,20 +478,20 @@ void AudioDriverCoreAudio::set_device(String device) { UInt32 deviceCount = size / sizeof(AudioDeviceID); for (UInt32 i = 0; i < deviceCount && !found; i++) { - prop.mScope = kAudioDevicePropertyScopeOutput; + prop.mScope = capture ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput; prop.mSelector = kAudioDevicePropertyStreamConfiguration; AudioObjectGetPropertyDataSize(audioDevices[i], &prop, 0, NULL, &size); AudioBufferList *bufferList = (AudioBufferList *)malloc(size); AudioObjectGetPropertyData(audioDevices[i], &prop, 0, NULL, &size, bufferList); - UInt32 outputChannelCount = 0; + UInt32 channelCount = 0; for (UInt32 j = 0; j < bufferList->mNumberBuffers; j++) - outputChannelCount += bufferList->mBuffers[j].mNumberChannels; + channelCount += bufferList->mBuffers[j].mNumberChannels; free(bufferList); - if (outputChannelCount >= 1) { + if (channelCount >= 1) { CFStringRef cfname; size = sizeof(CFStringRef); @@ -332,7 +504,7 @@ void AudioDriverCoreAudio::set_device(String device) { char *buffer = (char *)malloc(maxSize); if (CFStringGetCString(cfname, buffer, maxSize, kCFStringEncodingUTF8)) { String name = String(buffer) + " (" + itos(audioDevices[i]) + ")"; - if (name == device_name) { + if (name == device) { deviceId = audioDevices[i]; found = true; } @@ -348,7 +520,8 @@ void AudioDriverCoreAudio::set_device(String device) { if (!found) { // If we haven't found the desired device get the system default one UInt32 size = sizeof(AudioDeviceID); - AudioObjectPropertyAddress property = { kAudioHardwarePropertyDefaultOutputDevice, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; + UInt32 elem = capture ? kAudioHardwarePropertyDefaultInputDevice : kAudioHardwarePropertyDefaultOutputDevice; + AudioObjectPropertyAddress property = { elem, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; OSStatus result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &property, 0, NULL, &size, &deviceId); ERR_FAIL_COND(result != noErr); @@ -357,79 +530,52 @@ void AudioDriverCoreAudio::set_device(String device) { } if (found) { - OSStatus result = AudioUnitSetProperty(audio_unit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &deviceId, sizeof(AudioDeviceID)); + OSStatus result = AudioUnitSetProperty(audio_unit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, capture ? kInputBus : kOutputBus, &deviceId, sizeof(AudioDeviceID)); ERR_FAIL_COND(result != noErr); + + // Reset audio input to keep synchronisation. + input_position = 0; + input_size = 0; } } -#endif - -void AudioDriverCoreAudio::lock() { - if (mutex) - mutex->lock(); -}; - -void AudioDriverCoreAudio::unlock() { - if (mutex) - mutex->unlock(); -}; +Array AudioDriverCoreAudio::get_device_list() { -bool AudioDriverCoreAudio::try_lock() { - if (mutex) - return mutex->try_lock() == OK; - return true; + return _get_device_list(); } -void AudioDriverCoreAudio::finish() { - OSStatus result; +String AudioDriverCoreAudio::get_device() { - lock(); + return device_name; +} - AURenderCallbackStruct callback; - zeromem(&callback, sizeof(AURenderCallbackStruct)); - result = AudioUnitSetProperty(audio_unit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, kOutputBus, &callback, sizeof(callback)); - if (result != noErr) { - ERR_PRINT("AudioUnitSetProperty failed"); - } +void AudioDriverCoreAudio::set_device(String device) { + device_name = device; if (active) { - result = AudioOutputUnitStop(audio_unit); - if (result != noErr) { - ERR_PRINT("AudioOutputUnitStop failed"); - } - - active = false; + _set_device(device_name); } +} - result = AudioUnitUninitialize(audio_unit); - if (result != noErr) { - ERR_PRINT("AudioUnitUninitialize failed"); +void AudioDriverCoreAudio::capture_set_device(const String &p_name) { + + capture_device_name = p_name; + if (active) { + _set_device(capture_device_name, true); } +} -#ifdef OSX_ENABLED - AudioObjectPropertyAddress prop; - prop.mSelector = kAudioHardwarePropertyDefaultOutputDevice; - prop.mScope = kAudioObjectPropertyScopeGlobal; - prop.mElement = kAudioObjectPropertyElementMaster; +Array AudioDriverCoreAudio::capture_get_device_list() { - result = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &prop, &output_device_address_cb, this); - if (result != noErr) { - ERR_PRINT("AudioObjectRemovePropertyListener failed"); - } -#endif + return _get_device_list(true); +} - result = AudioComponentInstanceDispose(audio_unit); - if (result != noErr) { - ERR_PRINT("AudioComponentInstanceDispose failed"); - } +String AudioDriverCoreAudio::capture_get_device() { - unlock(); + return capture_device_name; +} - if (mutex) { - memdelete(mutex); - mutex = NULL; - } -}; +#endif AudioDriverCoreAudio::AudioDriverCoreAudio() { active = false; @@ -437,14 +583,15 @@ AudioDriverCoreAudio::AudioDriverCoreAudio() { mix_rate = 0; channels = 2; + capture_channels = 2; - buffer_size = 0; buffer_frames = 0; samples_in.clear(); device_name = "Default"; -}; + capture_device_name = "Default"; +} AudioDriverCoreAudio::~AudioDriverCoreAudio(){}; diff --git a/drivers/coreaudio/audio_driver_coreaudio.h b/drivers/coreaudio/audio_driver_coreaudio.h index 99c910498e..d3f7c8d596 100644 --- a/drivers/coreaudio/audio_driver_coreaudio.h +++ b/drivers/coreaudio/audio_driver_coreaudio.h @@ -48,15 +48,24 @@ class AudioDriverCoreAudio : public AudioDriver { Mutex *mutex; String device_name; + String capture_device_name; int mix_rate; unsigned int channels; + unsigned int capture_channels; unsigned int buffer_frames; - unsigned int buffer_size; Vector<int32_t> samples_in; + Vector<int16_t> input_buf; #ifdef OSX_ENABLED + Array _get_device_list(bool capture = false); + void _set_device(const String &device, bool capture = false); + + static OSStatus input_device_address_cb(AudioObjectID inObjectID, + UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses, + void *inClientData); + static OSStatus output_device_address_cb(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses, void *inClientData); @@ -68,6 +77,12 @@ class AudioDriverCoreAudio : public AudioDriver { UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData); + static OSStatus input_callback(void *inRefCon, + AudioUnitRenderActionFlags *ioActionFlags, + const AudioTimeStamp *inTimeStamp, + UInt32 inBusNumber, UInt32 inNumberFrames, + AudioBufferList *ioData); + public: const char *get_name() const { return "CoreAudio"; @@ -77,18 +92,27 @@ public: virtual void start(); virtual int get_mix_rate() const; virtual SpeakerMode get_speaker_mode() const; -#ifdef OSX_ENABLED - virtual Array get_device_list(); - virtual String get_device(); - virtual void set_device(String device); -#endif + virtual void lock(); virtual void unlock(); virtual void finish(); + virtual Error capture_start(); + virtual Error capture_stop(); + bool try_lock(); void stop(); +#ifdef OSX_ENABLED + virtual Array get_device_list(); + virtual String get_device(); + virtual void set_device(String device); + + virtual Array capture_get_device_list(); + virtual void capture_set_device(const String &p_name); + virtual String capture_get_device(); +#endif + AudioDriverCoreAudio(); ~AudioDriverCoreAudio(); }; diff --git a/drivers/coremidi/SCsub b/drivers/coremidi/SCsub new file mode 100644 index 0000000000..233593b0f9 --- /dev/null +++ b/drivers/coremidi/SCsub @@ -0,0 +1,8 @@ +#!/usr/bin/env python + +Import('env') + +# Driver source files +env.add_source_files(env.drivers_sources, "*.cpp") + +Export('env') diff --git a/drivers/coremidi/core_midi.cpp b/drivers/coremidi/core_midi.cpp new file mode 100644 index 0000000000..3619be4a8e --- /dev/null +++ b/drivers/coremidi/core_midi.cpp @@ -0,0 +1,105 @@ +/*************************************************************************/ +/* core_midi.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifdef COREMIDI_ENABLED + +#include "core_midi.h" +#include "print_string.h" + +#include <CoreAudio/HostTime.h> +#include <CoreServices/CoreServices.h> + +void MIDIDriverCoreMidi::read(const MIDIPacketList *packet_list, void *read_proc_ref_con, void *src_conn_ref_con) { + MIDIPacket *packet = const_cast<MIDIPacket *>(packet_list->packet); + for (int i = 0; i < packet_list->numPackets; i++) { + receive_input_packet(packet->timeStamp, packet->data, packet->length); + packet = MIDIPacketNext(packet); + } +} + +Error MIDIDriverCoreMidi::open() { + + CFStringRef name = CFStringCreateWithCString(NULL, "Godot", kCFStringEncodingASCII); + OSStatus result = MIDIClientCreate(name, NULL, NULL, &client); + CFRelease(name); + if (result != noErr) { + ERR_PRINTS("MIDIClientCreate failed: " + String(GetMacOSStatusErrorString(result))); + return ERR_CANT_OPEN; + } + + result = MIDIInputPortCreate(client, CFSTR("Godot Input"), MIDIDriverCoreMidi::read, (void *)this, &port_in); + if (result != noErr) { + ERR_PRINTS("MIDIInputPortCreate failed: " + String(GetMacOSStatusErrorString(result))); + return ERR_CANT_OPEN; + } + + int sources = MIDIGetNumberOfSources(); + for (int i = 0; i < sources; i++) { + + MIDIEndpointRef source = MIDIGetSource(i); + if (source != NULL) { + MIDIPortConnectSource(port_in, source, (void *)this); + connected_sources.insert(i, source); + } + } + + return OK; +} + +void MIDIDriverCoreMidi::close() { + + for (int i = 0; i < connected_sources.size(); i++) { + MIDIEndpointRef source = connected_sources[i]; + MIDIPortDisconnectSource(port_in, source); + } + connected_sources.clear(); + + if (port_in != 0) { + MIDIPortDispose(port_in); + port_in = 0; + } + + if (client != 0) { + MIDIClientDispose(client); + client = 0; + } +} + +MIDIDriverCoreMidi::MIDIDriverCoreMidi() { + + client = 0; +} + +MIDIDriverCoreMidi::~MIDIDriverCoreMidi() { + + close(); +} + +#endif diff --git a/drivers/coremidi/core_midi.h b/drivers/coremidi/core_midi.h new file mode 100644 index 0000000000..fd35e12f4b --- /dev/null +++ b/drivers/coremidi/core_midi.h @@ -0,0 +1,61 @@ +/*************************************************************************/ +/* core_midi.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifdef COREMIDI_ENABLED + +#ifndef CORE_MIDI_H +#define CORE_MIDI_H + +#include <stdio.h> + +#include <CoreMIDI/CoreMIDI.h> + +#include "core/vector.h" +#include "os/midi_driver.h" + +class MIDIDriverCoreMidi : public MIDIDriver { + + MIDIClientRef client; + MIDIPortRef port_in; + + Vector<MIDIEndpointRef> connected_sources; + + static void read(const MIDIPacketList *packet_list, void *read_proc_ref_con, void *src_conn_ref_con); + +public: + virtual Error open(); + virtual void close(); + + MIDIDriverCoreMidi(); + virtual ~MIDIDriverCoreMidi(); +}; + +#endif +#endif diff --git a/drivers/dummy/rasterizer_dummy.h b/drivers/dummy/rasterizer_dummy.h index 068a14cb8a..e39ec915fc 100644 --- a/drivers/dummy/rasterizer_dummy.h +++ b/drivers/dummy/rasterizer_dummy.h @@ -154,7 +154,8 @@ public: ERR_FAIL_COND_V(!texture, RID()); return texture_owner.make_rid(texture); } - void texture_allocate(RID p_texture, int p_width, int p_height, Image::Format p_format, uint32_t p_flags = VS::TEXTURE_FLAGS_DEFAULT) { + + void texture_allocate(RID p_texture, int p_width, int p_height, int p_depth_3d, Image::Format p_format, VisualServer::TextureType p_type = VS::TEXTURE_TYPE_2D, uint32_t p_flags = VS::TEXTURE_FLAGS_DEFAULT) { DummyTexture *t = texture_owner.getornull(p_texture); ERR_FAIL_COND(!t); t->width = p_width; @@ -164,7 +165,7 @@ public: t->image = Ref<Image>(memnew(Image)); t->image->create(p_width, p_height, false, p_format); } - void texture_set_data(RID p_texture, const Ref<Image> &p_image, VS::CubeMapSide p_cube_side = VS::CUBEMAP_LEFT) { + void texture_set_data(RID p_texture, const Ref<Image> &p_image, int p_level) { DummyTexture *t = texture_owner.getornull(p_texture); ERR_FAIL_COND(!t); t->width = p_image->get_width(); @@ -173,7 +174,7 @@ public: t->image->create(t->width, t->height, false, t->format, p_image->get_data()); } - void texture_set_data_partial(RID p_texture, const Ref<Image> &p_image, int src_x, int src_y, int src_w, int src_h, int dst_x, int dst_y, int p_dst_mip, VS::CubeMapSide p_cube_side) { + void texture_set_data_partial(RID p_texture, const Ref<Image> &p_image, int src_x, int src_y, int src_w, int src_h, int dst_x, int dst_y, int p_dst_mip, int p_level) { DummyTexture *t = texture_owner.get(p_texture); ERR_FAIL_COND(!t); @@ -186,7 +187,7 @@ public: t->image->blit_rect(p_image, Rect2(src_x, src_y, src_w, src_h), Vector2(dst_x, dst_y)); } - Ref<Image> texture_get_data(RID p_texture, VS::CubeMapSide p_cube_side = VS::CUBEMAP_LEFT) const { + Ref<Image> texture_get_data(RID p_texture, int p_level) const { DummyTexture *t = texture_owner.getornull(p_texture); ERR_FAIL_COND_V(!t, Ref<Image>()); return t->image; @@ -206,10 +207,13 @@ public: ERR_FAIL_COND_V(!t, Image::FORMAT_RGB8); return t->format; } + + VisualServer::TextureType texture_get_type(RID p_texture) const { return VS::TEXTURE_TYPE_2D; } uint32_t texture_get_texid(RID p_texture) const { return 0; } uint32_t texture_get_width(RID p_texture) const { return 0; } uint32_t texture_get_height(RID p_texture) const { return 0; } - void texture_set_size_override(RID p_texture, int p_width, int p_height) {} + uint32_t texture_get_depth(RID p_texture) const { return 0; } + void texture_set_size_override(RID p_texture, int p_width, int p_height, int p_depth_3d) {} void texture_set_path(RID p_texture, const String &p_path) { DummyTexture *t = texture_owner.getornull(p_texture); @@ -235,6 +239,7 @@ public: void textures_keep_original(bool p_enable) {} void texture_set_proxy(RID p_proxy, RID p_base) {} + void texture_set_force_redraw_if_visible(RID p_texture, bool p_enable) {} /* SKY API */ @@ -288,7 +293,7 @@ public: ERR_FAIL_COND(!m); m->surfaces.push_back(DummySurface()); - DummySurface *s = &m->surfaces[m->surfaces.size() - 1]; + DummySurface *s = &m->surfaces.write[m->surfaces.size() - 1]; s->format = p_format; s->primitive = p_primitive; s->array = p_array; @@ -776,7 +781,7 @@ public: void set_boot_image(const Ref<Image> &p_image, const Color &p_color, bool p_scale) {} void initialize() {} - void begin_frame() {} + void begin_frame(double frame_step) {} void set_current_render_target(RID p_render_target) {} void restore_render_target() {} void clear_render_target(const Color &p_color) {} diff --git a/drivers/dummy/texture_loader_dummy.cpp b/drivers/dummy/texture_loader_dummy.cpp index 6d3e176bbb..b099019d17 100644 --- a/drivers/dummy/texture_loader_dummy.cpp +++ b/drivers/dummy/texture_loader_dummy.cpp @@ -45,10 +45,6 @@ RES ResourceFormatDummyTexture::load(const String &p_path, const String &p_origi dstbuff.resize(rowsize * height); - PoolVector<uint8_t>::Write dstbuff_write = dstbuff.write(); - - uint8_t *data = dstbuff_write.ptr(); - uint8_t **row_p = memnew_arr(uint8_t *, height); for (unsigned int i = 0; i < height; i++) { diff --git a/drivers/gles2/rasterizer_canvas_gles2.cpp b/drivers/gles2/rasterizer_canvas_gles2.cpp index d5232a6511..5b04517394 100644 --- a/drivers/gles2/rasterizer_canvas_gles2.cpp +++ b/drivers/gles2/rasterizer_canvas_gles2.cpp @@ -54,11 +54,28 @@ void RasterizerCanvasGLES2::_set_uniforms() { state.canvas_shader.set_uniform(CanvasShaderGLES2::EXTRA_MATRIX, state.uniforms.extra_matrix); state.canvas_shader.set_uniform(CanvasShaderGLES2::FINAL_MODULATE, state.uniforms.final_modulate); + + state.canvas_shader.set_uniform(CanvasShaderGLES2::TIME, storage->frame.time[0]); + + if (storage->frame.current_rt) { + Vector2 screen_pixel_size; + screen_pixel_size.x = 1.0 / storage->frame.current_rt->width; + screen_pixel_size.y = 1.0 / storage->frame.current_rt->height; + + state.canvas_shader.set_uniform(CanvasShaderGLES2::SCREEN_PIXEL_SIZE, screen_pixel_size); + } } void RasterizerCanvasGLES2::canvas_begin() { + state.canvas_shader.bind(); + if (storage->frame.current_rt) { + glBindFramebuffer(GL_FRAMEBUFFER, storage->frame.current_rt->fbo); + glColorMask(1, 1, 1, 1); + } + if (storage->frame.clear_request) { + glColorMask(true, true, true, true); glClearColor(storage->frame.clear_request_color.r, storage->frame.clear_request_color.g, storage->frame.clear_request_color.b, @@ -67,10 +84,12 @@ void RasterizerCanvasGLES2::canvas_begin() { storage->frame.clear_request = false; } + /* if (storage->frame.current_rt) { glBindFramebuffer(GL_FRAMEBUFFER, storage->frame.current_rt->fbo); glColorMask(1, 1, 1, 1); } + */ reset_canvas(); @@ -133,18 +152,22 @@ RasterizerStorageGLES2::Texture *RasterizerCanvasGLES2::_bind_canvas_texture(con state.current_tex = RID(); state.current_tex_ptr = NULL; - glActiveTexture(GL_TEXTURE0); + glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 1); glBindTexture(GL_TEXTURE_2D, storage->resources.white_tex); } else { texture = texture->get_ptr(); + if (texture->redraw_if_visible) { + VisualServerRaster::redraw_request(); + } + if (texture->render_target) { texture->render_target->used_in_frame = true; } - glActiveTexture(GL_TEXTURE0); + glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 1); glBindTexture(GL_TEXTURE_2D, texture->tex_id); state.current_tex = p_texture; @@ -156,7 +179,7 @@ RasterizerStorageGLES2::Texture *RasterizerCanvasGLES2::_bind_canvas_texture(con state.current_tex = RID(); state.current_tex_ptr = NULL; - glActiveTexture(GL_TEXTURE0); + glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 1); glBindTexture(GL_TEXTURE_2D, storage->resources.white_tex); } @@ -308,7 +331,7 @@ void RasterizerCanvasGLES2::_draw_gui_primitive(int p_points, const Vector2 *p_v glBindBuffer(GL_ARRAY_BUFFER, 0); } -void RasterizerCanvasGLES2::_canvas_item_render_commands(Item *p_item, Item *current_clip, bool &reclip) { +void RasterizerCanvasGLES2::_canvas_item_render_commands(Item *p_item, Item *current_clip, bool &reclip, RasterizerStorageGLES2::Material *p_material) { int command_count = p_item->commands.size(); Item::Command **commands = p_item->commands.ptrw(); @@ -325,9 +348,10 @@ void RasterizerCanvasGLES2::_canvas_item_render_commands(Item *p_item, Item *cur state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_TEXTURE_RECT, false); state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_UV_ATTRIBUTE, false); - state.canvas_shader.bind(); - - _set_uniforms(); + if (state.canvas_shader.bind()) { + _set_uniforms(); + state.canvas_shader.use_material((void *)p_material); + } _bind_canvas_texture(RID(), RID()); @@ -355,7 +379,6 @@ void RasterizerCanvasGLES2::_canvas_item_render_commands(Item *p_item, Item *cur _draw_gui_primitive(4, verts, NULL, NULL); } - } break; case Item::Command::TYPE_RECT: { @@ -369,8 +392,10 @@ void RasterizerCanvasGLES2::_canvas_item_render_commands(Item *p_item, Item *cur state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_TEXTURE_RECT, true); state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_UV_ATTRIBUTE, false); - if (state.canvas_shader.bind()) + if (state.canvas_shader.bind()) { _set_uniforms(); + state.canvas_shader.use_material((void *)p_material); + } RasterizerStorageGLES2::Texture *tex = _bind_canvas_texture(r->texture, r->normal_map); @@ -405,8 +430,6 @@ void RasterizerCanvasGLES2::_canvas_item_render_commands(Item *p_item, Item *cur Rect2 dst_rect = Rect2(r->rect.position, r->rect.size); - state.canvas_shader.set_uniform(CanvasShaderGLES2::COLOR_TEXPIXEL_SIZE, texpixel_size); - if (dst_rect.size.width < 0) { dst_rect.position.x += dst_rect.size.width; dst_rect.size.width *= -1; @@ -452,8 +475,10 @@ void RasterizerCanvasGLES2::_canvas_item_render_commands(Item *p_item, Item *cur state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_TEXTURE_RECT, false); state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_UV_ATTRIBUTE, true); - if (state.canvas_shader.bind()) + if (state.canvas_shader.bind()) { _set_uniforms(); + state.canvas_shader.use_material((void *)p_material); + } glDisableVertexAttribArray(VS::ARRAY_COLOR); glVertexAttrib4fv(VS::ARRAY_COLOR, np->color.components); @@ -470,8 +495,16 @@ void RasterizerCanvasGLES2::_canvas_item_render_commands(Item *p_item, Item *cur // state.canvas_shader.set_uniform(CanvasShaderGLES2::MODELVIEW_MATRIX, state.uniforms.modelview_matrix); state.canvas_shader.set_uniform(CanvasShaderGLES2::COLOR_TEXPIXEL_SIZE, texpixel_size); + Rect2 source = np->source; + if (source.size.x == 0 && source.size.y == 0) { + source.size.x = tex->width; + source.size.y = tex->height; + } + // prepare vertex buffer + // this buffer contains [ POS POS UV UV ] * + float buffer[16 * 2 + 16 * 2]; { @@ -481,106 +514,106 @@ void RasterizerCanvasGLES2::_canvas_item_render_commands(Item *p_item, Item *cur buffer[(0 * 4 * 4) + 0] = np->rect.position.x; buffer[(0 * 4 * 4) + 1] = np->rect.position.y; - buffer[(0 * 4 * 4) + 2] = np->source.position.x * texpixel_size.x; - buffer[(0 * 4 * 4) + 3] = np->source.position.y * texpixel_size.y; + buffer[(0 * 4 * 4) + 2] = source.position.x * texpixel_size.x; + buffer[(0 * 4 * 4) + 3] = source.position.y * texpixel_size.y; buffer[(0 * 4 * 4) + 4] = np->rect.position.x + np->margin[MARGIN_LEFT]; buffer[(0 * 4 * 4) + 5] = np->rect.position.y; - buffer[(0 * 4 * 4) + 6] = (np->source.position.x + np->margin[MARGIN_LEFT]) * texpixel_size.x; - buffer[(0 * 4 * 4) + 7] = np->source.position.y * texpixel_size.y; + buffer[(0 * 4 * 4) + 6] = (source.position.x + np->margin[MARGIN_LEFT]) * texpixel_size.x; + buffer[(0 * 4 * 4) + 7] = source.position.y * texpixel_size.y; buffer[(0 * 4 * 4) + 8] = np->rect.position.x + np->rect.size.x - np->margin[MARGIN_RIGHT]; buffer[(0 * 4 * 4) + 9] = np->rect.position.y; - buffer[(0 * 4 * 4) + 10] = (np->source.position.x + np->source.size.x - np->margin[MARGIN_RIGHT]) * texpixel_size.x; - buffer[(0 * 4 * 4) + 11] = np->source.position.y * texpixel_size.y; + buffer[(0 * 4 * 4) + 10] = (source.position.x + source.size.x - np->margin[MARGIN_RIGHT]) * texpixel_size.x; + buffer[(0 * 4 * 4) + 11] = source.position.y * texpixel_size.y; buffer[(0 * 4 * 4) + 12] = np->rect.position.x + np->rect.size.x; buffer[(0 * 4 * 4) + 13] = np->rect.position.y; - buffer[(0 * 4 * 4) + 14] = (np->source.position.x + np->source.size.x) * texpixel_size.x; - buffer[(0 * 4 * 4) + 15] = np->source.position.y * texpixel_size.y; + buffer[(0 * 4 * 4) + 14] = (source.position.x + source.size.x) * texpixel_size.x; + buffer[(0 * 4 * 4) + 15] = source.position.y * texpixel_size.y; // second row buffer[(1 * 4 * 4) + 0] = np->rect.position.x; buffer[(1 * 4 * 4) + 1] = np->rect.position.y + np->margin[MARGIN_TOP]; - buffer[(1 * 4 * 4) + 2] = np->source.position.x * texpixel_size.x; - buffer[(1 * 4 * 4) + 3] = (np->source.position.y + np->margin[MARGIN_TOP]) * texpixel_size.y; + buffer[(1 * 4 * 4) + 2] = source.position.x * texpixel_size.x; + buffer[(1 * 4 * 4) + 3] = (source.position.y + np->margin[MARGIN_TOP]) * texpixel_size.y; buffer[(1 * 4 * 4) + 4] = np->rect.position.x + np->margin[MARGIN_LEFT]; buffer[(1 * 4 * 4) + 5] = np->rect.position.y + np->margin[MARGIN_TOP]; - buffer[(1 * 4 * 4) + 6] = (np->source.position.x + np->margin[MARGIN_LEFT]) * texpixel_size.x; - buffer[(1 * 4 * 4) + 7] = (np->source.position.y + np->margin[MARGIN_TOP]) * texpixel_size.y; + buffer[(1 * 4 * 4) + 6] = (source.position.x + np->margin[MARGIN_LEFT]) * texpixel_size.x; + buffer[(1 * 4 * 4) + 7] = (source.position.y + np->margin[MARGIN_TOP]) * texpixel_size.y; buffer[(1 * 4 * 4) + 8] = np->rect.position.x + np->rect.size.x - np->margin[MARGIN_RIGHT]; buffer[(1 * 4 * 4) + 9] = np->rect.position.y + np->margin[MARGIN_TOP]; - buffer[(1 * 4 * 4) + 10] = (np->source.position.x + np->source.size.x - np->margin[MARGIN_RIGHT]) * texpixel_size.x; - buffer[(1 * 4 * 4) + 11] = (np->source.position.y + np->margin[MARGIN_TOP]) * texpixel_size.y; + buffer[(1 * 4 * 4) + 10] = (source.position.x + source.size.x - np->margin[MARGIN_RIGHT]) * texpixel_size.x; + buffer[(1 * 4 * 4) + 11] = (source.position.y + np->margin[MARGIN_TOP]) * texpixel_size.y; buffer[(1 * 4 * 4) + 12] = np->rect.position.x + np->rect.size.x; buffer[(1 * 4 * 4) + 13] = np->rect.position.y + np->margin[MARGIN_TOP]; - buffer[(1 * 4 * 4) + 14] = (np->source.position.x + np->source.size.x) * texpixel_size.x; - buffer[(1 * 4 * 4) + 15] = (np->source.position.y + np->margin[MARGIN_TOP]) * texpixel_size.y; + buffer[(1 * 4 * 4) + 14] = (source.position.x + source.size.x) * texpixel_size.x; + buffer[(1 * 4 * 4) + 15] = (source.position.y + np->margin[MARGIN_TOP]) * texpixel_size.y; // thrid row buffer[(2 * 4 * 4) + 0] = np->rect.position.x; buffer[(2 * 4 * 4) + 1] = np->rect.position.y + np->rect.size.y - np->margin[MARGIN_BOTTOM]; - buffer[(2 * 4 * 4) + 2] = np->source.position.x * texpixel_size.x; - buffer[(2 * 4 * 4) + 3] = (np->source.position.y + np->source.size.y - np->margin[MARGIN_BOTTOM]) * texpixel_size.y; + buffer[(2 * 4 * 4) + 2] = source.position.x * texpixel_size.x; + buffer[(2 * 4 * 4) + 3] = (source.position.y + source.size.y - np->margin[MARGIN_BOTTOM]) * texpixel_size.y; buffer[(2 * 4 * 4) + 4] = np->rect.position.x + np->margin[MARGIN_LEFT]; buffer[(2 * 4 * 4) + 5] = np->rect.position.y + np->rect.size.y - np->margin[MARGIN_BOTTOM]; - buffer[(2 * 4 * 4) + 6] = (np->source.position.x + np->margin[MARGIN_LEFT]) * texpixel_size.x; - buffer[(2 * 4 * 4) + 7] = (np->source.position.y + np->source.size.y - np->margin[MARGIN_BOTTOM]) * texpixel_size.y; + buffer[(2 * 4 * 4) + 6] = (source.position.x + np->margin[MARGIN_LEFT]) * texpixel_size.x; + buffer[(2 * 4 * 4) + 7] = (source.position.y + source.size.y - np->margin[MARGIN_BOTTOM]) * texpixel_size.y; buffer[(2 * 4 * 4) + 8] = np->rect.position.x + np->rect.size.x - np->margin[MARGIN_RIGHT]; buffer[(2 * 4 * 4) + 9] = np->rect.position.y + np->rect.size.y - np->margin[MARGIN_BOTTOM]; - buffer[(2 * 4 * 4) + 10] = (np->source.position.x + np->source.size.x - np->margin[MARGIN_RIGHT]) * texpixel_size.x; - buffer[(2 * 4 * 4) + 11] = (np->source.position.y + np->source.size.y - np->margin[MARGIN_BOTTOM]) * texpixel_size.y; + buffer[(2 * 4 * 4) + 10] = (source.position.x + source.size.x - np->margin[MARGIN_RIGHT]) * texpixel_size.x; + buffer[(2 * 4 * 4) + 11] = (source.position.y + source.size.y - np->margin[MARGIN_BOTTOM]) * texpixel_size.y; buffer[(2 * 4 * 4) + 12] = np->rect.position.x + np->rect.size.x; buffer[(2 * 4 * 4) + 13] = np->rect.position.y + np->rect.size.y - np->margin[MARGIN_BOTTOM]; - buffer[(2 * 4 * 4) + 14] = (np->source.position.x + np->source.size.x) * texpixel_size.x; - buffer[(2 * 4 * 4) + 15] = (np->source.position.y + np->source.size.y - np->margin[MARGIN_BOTTOM]) * texpixel_size.y; + buffer[(2 * 4 * 4) + 14] = (source.position.x + source.size.x) * texpixel_size.x; + buffer[(2 * 4 * 4) + 15] = (source.position.y + source.size.y - np->margin[MARGIN_BOTTOM]) * texpixel_size.y; // fourth row buffer[(3 * 4 * 4) + 0] = np->rect.position.x; buffer[(3 * 4 * 4) + 1] = np->rect.position.y + np->rect.size.y; - buffer[(3 * 4 * 4) + 2] = np->source.position.x * texpixel_size.x; - buffer[(3 * 4 * 4) + 3] = (np->source.position.y + np->source.size.y) * texpixel_size.y; + buffer[(3 * 4 * 4) + 2] = source.position.x * texpixel_size.x; + buffer[(3 * 4 * 4) + 3] = (source.position.y + source.size.y) * texpixel_size.y; buffer[(3 * 4 * 4) + 4] = np->rect.position.x + np->margin[MARGIN_LEFT]; buffer[(3 * 4 * 4) + 5] = np->rect.position.y + np->rect.size.y; - buffer[(3 * 4 * 4) + 6] = (np->source.position.x + np->margin[MARGIN_LEFT]) * texpixel_size.x; - buffer[(3 * 4 * 4) + 7] = (np->source.position.y + np->source.size.y) * texpixel_size.y; + buffer[(3 * 4 * 4) + 6] = (source.position.x + np->margin[MARGIN_LEFT]) * texpixel_size.x; + buffer[(3 * 4 * 4) + 7] = (source.position.y + source.size.y) * texpixel_size.y; buffer[(3 * 4 * 4) + 8] = np->rect.position.x + np->rect.size.x - np->margin[MARGIN_RIGHT]; buffer[(3 * 4 * 4) + 9] = np->rect.position.y + np->rect.size.y; - buffer[(3 * 4 * 4) + 10] = (np->source.position.x + np->source.size.x - np->margin[MARGIN_RIGHT]) * texpixel_size.x; - buffer[(3 * 4 * 4) + 11] = (np->source.position.y + np->source.size.y) * texpixel_size.y; + buffer[(3 * 4 * 4) + 10] = (source.position.x + source.size.x - np->margin[MARGIN_RIGHT]) * texpixel_size.x; + buffer[(3 * 4 * 4) + 11] = (source.position.y + source.size.y) * texpixel_size.y; buffer[(3 * 4 * 4) + 12] = np->rect.position.x + np->rect.size.x; buffer[(3 * 4 * 4) + 13] = np->rect.position.y + np->rect.size.y; - buffer[(3 * 4 * 4) + 14] = (np->source.position.x + np->source.size.x) * texpixel_size.x; - buffer[(3 * 4 * 4) + 15] = (np->source.position.y + np->source.size.y) * texpixel_size.y; + buffer[(3 * 4 * 4) + 14] = (source.position.x + source.size.x) * texpixel_size.x; + buffer[(3 * 4 * 4) + 15] = (source.position.y + source.size.y) * texpixel_size.y; - // print_line(String::num((np->source.position.y + np->source.size.y) * texpixel_size.y)); + // print_line(String::num((source.position.y + source.size.y) * texpixel_size.y)); } glBindBuffer(GL_ARRAY_BUFFER, data.ninepatch_vertices); @@ -608,8 +641,10 @@ void RasterizerCanvasGLES2::_canvas_item_render_commands(Item *p_item, Item *cur state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_TEXTURE_RECT, false); state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_UV_ATTRIBUTE, false); - if (state.canvas_shader.bind()) + if (state.canvas_shader.bind()) { _set_uniforms(); + state.canvas_shader.use_material((void *)p_material); + } static const int num_points = 32; @@ -637,8 +672,10 @@ void RasterizerCanvasGLES2::_canvas_item_render_commands(Item *p_item, Item *cur state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_TEXTURE_RECT, false); state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_UV_ATTRIBUTE, true); - if (state.canvas_shader.bind()) + if (state.canvas_shader.bind()) { _set_uniforms(); + state.canvas_shader.use_material((void *)p_material); + } RasterizerStorageGLES2::Texture *texture = _bind_canvas_texture(polygon->texture, polygon->normal_map); @@ -656,8 +693,12 @@ void RasterizerCanvasGLES2::_canvas_item_render_commands(Item *p_item, Item *cur state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_TEXTURE_RECT, false); state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_UV_ATTRIBUTE, false); - if (state.canvas_shader.bind()) + if (state.canvas_shader.bind()) { _set_uniforms(); + state.canvas_shader.use_material((void *)p_material); + } + + _bind_canvas_texture(RID(), RID()); if (pline->triangles.size()) { _draw_generic(GL_TRIANGLE_STRIP, pline->triangles.size(), pline->triangles.ptr(), NULL, pline->triangle_colors.ptr(), pline->triangle_colors.size() == 1); @@ -685,8 +726,10 @@ void RasterizerCanvasGLES2::_canvas_item_render_commands(Item *p_item, Item *cur state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_TEXTURE_RECT, false); state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_UV_ATTRIBUTE, true); - if (state.canvas_shader.bind()) + if (state.canvas_shader.bind()) { _set_uniforms(); + state.canvas_shader.use_material((void *)p_material); + } ERR_CONTINUE(primitive->points.size() < 1); @@ -755,42 +798,6 @@ void RasterizerCanvasGLES2::_canvas_item_render_commands(Item *p_item, Item *cur void RasterizerCanvasGLES2::_copy_texscreen(const Rect2 &p_rect) { // This isn't really working yet, so disabling for now. - - /* - glDisable(GL_BLEND); - - state.canvas_texscreen_used = true; - - Vector2 wh(storage->frame.current_rt->width, storage->frame.current_rt->height); - Color copy_section(p_rect.position.x / wh.x, p_rect.position.y / wh.y, p_rect.size.x / wh.x, p_rect.size.y / wh.y); - - if (p_rect != Rect2()) { - // only use section - - storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_COPY_SECTION, true); - } - - - storage->shaders.copy.bind(); - storage->shaders.copy.set_uniform(CopyShaderGLES2::COPY_SECTION, copy_section); - - _bind_quad_buffer(); - - glBindFramebuffer(GL_FRAMEBUFFER, storage->frame.current_rt->copy_screen_effect.fbo); - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, storage->frame.current_rt->color); - - glDrawArrays(GL_TRIANGLE_FAN, 0, 4); - glDisableVertexAttribArray(VS::ARRAY_VERTEX); - glBindBuffer(GL_ARRAY_BUFFER, 0); - - glBindFramebuffer(GL_FRAMEBUFFER, storage->frame.current_rt->fbo); - - state.canvas_shader.bind(); - _bind_canvas_texture(state.current_tex, state.current_normal); - - glEnable(GL_BLEND); - */ } void RasterizerCanvasGLES2::canvas_render_items(Item *p_item_list, int p_z, const Color &p_modulate, Light *p_light, const Transform2D &p_base_transform) { @@ -846,10 +853,10 @@ void RasterizerCanvasGLES2::canvas_render_items(Item *p_item_list, int p_z, cons Item *material_owner = ci->material_owner ? ci->material_owner : ci; RID material = material_owner->material; + RasterizerStorageGLES2::Material *material_ptr = storage->material_owner.getornull(material); if (material != canvas_last_material || rebind_shader) { - RasterizerStorageGLES2::Material *material_ptr = storage->material_owner.getornull(material); RasterizerStorageGLES2::Shader *shader_ptr = NULL; if (material_ptr) { @@ -876,15 +883,15 @@ void RasterizerCanvasGLES2::canvas_render_items(Item *p_item_list, int p_z, cons } int tc = material_ptr->textures.size(); - RID *textures = material_ptr->textures.ptrw(); + Pair<StringName, RID> *textures = material_ptr->textures.ptrw(); ShaderLanguage::ShaderNode::Uniform::Hint *texture_hints = shader_ptr->texture_hints.ptrw(); for (int i = 0; i < tc; i++) { - glActiveTexture(GL_TEXTURE2 + i); + glActiveTexture(GL_TEXTURE0 + i); - RasterizerStorageGLES2::Texture *t = storage->texture_owner.getornull(textures[i]); + RasterizerStorageGLES2::Texture *t = storage->texture_owner.getornull(textures[i].second); if (!t) { @@ -909,12 +916,18 @@ void RasterizerCanvasGLES2::canvas_render_items(Item *p_item_list, int p_z, cons t = t->get_ptr(); + if (t->redraw_if_visible) { + VisualServerRaster::redraw_request(); + } + glBindTexture(t->target, t->tex_id); } + } else { state.canvas_shader.set_custom_shader(0); state.canvas_shader.bind(); } + state.canvas_shader.use_material((void *)material_ptr); shader_cache = shader_ptr; @@ -924,7 +937,7 @@ void RasterizerCanvasGLES2::canvas_render_items(Item *p_item_list, int p_z, cons } int blend_mode = shader_cache ? shader_cache->canvas_item.blend_mode : RasterizerStorageGLES2::Shader::CanvasItem::BLEND_MODE_MIX; - bool unshaded = true || (shader_cache && blend_mode != RasterizerStorageGLES2::Shader::CanvasItem::BLEND_MODE_MIX); + bool unshaded = (shader_cache && blend_mode != RasterizerStorageGLES2::Shader::CanvasItem::BLEND_MODE_MIX); bool reclip = false; if (last_blend_mode != blend_mode) { @@ -969,7 +982,7 @@ void RasterizerCanvasGLES2::canvas_render_items(Item *p_item_list, int p_z, cons _set_uniforms(); - _canvas_item_render_commands(p_item_list, NULL, reclip); + _canvas_item_render_commands(p_item_list, NULL, reclip, material_ptr); rebind_shader = true; // hacked in for now. @@ -1014,8 +1027,8 @@ void RasterizerCanvasGLES2::reset_canvas() { // keeping this for now as there's nothing else that uses texture unit 2 // TODO ^ if (storage->frame.current_rt) { - glActiveTexture(GL_TEXTURE0 + 2); - glBindTexture(GL_TEXTURE_2D, storage->frame.current_rt->copy_screen_effect.color); + // glActiveTexture(GL_TEXTURE0 + 2); + // glBindTexture(GL_TEXTURE_2D, storage->frame.current_rt->copy_screen_effect.color); } glBindBuffer(GL_ARRAY_BUFFER, 0); diff --git a/drivers/gles2/rasterizer_canvas_gles2.h b/drivers/gles2/rasterizer_canvas_gles2.h index 4eab8c6038..cda3ec79e7 100644 --- a/drivers/gles2/rasterizer_canvas_gles2.h +++ b/drivers/gles2/rasterizer_canvas_gles2.h @@ -103,7 +103,7 @@ public: _FORCE_INLINE_ void _draw_polygon(const int *p_indices, int p_index_count, int p_vertex_count, const Vector2 *p_vertices, const Vector2 *p_uvs, const Color *p_colors, bool p_singlecolor); _FORCE_INLINE_ void _draw_generic(GLuint p_primitive, int p_vertex_count, const Vector2 *p_vertices, const Vector2 *p_uvs, const Color *p_colors, bool p_singlecolor); - _FORCE_INLINE_ void _canvas_item_render_commands(Item *p_item, Item *current_clip, bool &reclip); + _FORCE_INLINE_ void _canvas_item_render_commands(Item *p_item, Item *current_clip, bool &reclip, RasterizerStorageGLES2::Material *p_material); _FORCE_INLINE_ void _copy_texscreen(const Rect2 &p_rect); virtual void canvas_render_items(Item *p_item_list, int p_z, const Color &p_modulate, Light *p_light, const Transform2D &p_base_transform); diff --git a/drivers/gles2/rasterizer_gles2.cpp b/drivers/gles2/rasterizer_gles2.cpp index ab48e682d6..8815205054 100644 --- a/drivers/gles2/rasterizer_gles2.cpp +++ b/drivers/gles2/rasterizer_gles2.cpp @@ -203,7 +203,7 @@ void RasterizerGLES2::initialize() { #endif // GLAD_ENABLED - // For debugging + // For debugging #ifdef GLES_OVER_GL if (GLAD_GL_ARB_debug_output) { glDebugMessageControlARB(_EXT_DEBUG_SOURCE_API_ARB, _EXT_DEBUG_TYPE_ERROR_ARB, _EXT_DEBUG_SEVERITY_HIGH_ARB, 0, NULL, GL_TRUE); @@ -227,21 +227,14 @@ void RasterizerGLES2::initialize() { scene->initialize(); } -void RasterizerGLES2::begin_frame() { - uint64_t tick = OS::get_singleton()->get_ticks_usec(); +void RasterizerGLES2::begin_frame(double frame_step) { + time_total += frame_step; - double delta = double(tick - prev_ticks) / 1000000.0; - delta *= Engine::get_singleton()->get_time_scale(); - - time_total += delta; - - if (delta == 0) { + if (frame_step == 0) { //to avoid hiccups - delta = 0.001; + frame_step = 0.001; } - prev_ticks = tick; - // double time_roll_over = GLOBAL_GET("rendering/limits/time/time_rollover_secs"); // if (time_total > time_roll_over) // time_total = 0; //roll over every day (should be customz @@ -251,9 +244,7 @@ void RasterizerGLES2::begin_frame() { storage->frame.time[2] = Math::fmod(time_total, 900); storage->frame.time[3] = Math::fmod(time_total, 60); storage->frame.count++; - storage->frame.delta = delta; - - storage->frame.prev_tick = tick; + storage->frame.delta = frame_step; storage->update_dirty_resources(); @@ -326,7 +317,7 @@ void RasterizerGLES2::set_boot_image(const Ref<Image> &p_image, const Color &p_c canvas->canvas_begin(); RID texture = storage->texture_create(); - storage->texture_allocate(texture, p_image->get_width(), p_image->get_height(), p_image->get_format(), VS::TEXTURE_FLAG_FILTER); + storage->texture_allocate(texture, p_image->get_width(), p_image->get_height(), 0, p_image->get_format(), VS::TEXTURE_TYPE_2D, VS::TEXTURE_FLAG_FILTER); storage->texture_set_data(texture, p_image); Rect2 imgrect(0, 0, p_image->get_width(), p_image->get_height()); @@ -336,7 +327,7 @@ void RasterizerGLES2::set_boot_image(const Ref<Image> &p_image, const Color &p_c screenrect.position += ((Size2(window_w, window_h) - screenrect.size) / 2.0).floor(); RasterizerStorageGLES2::Texture *t = storage->texture_owner.get(texture); - glActiveTexture(GL_TEXTURE0); + glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 1); glBindTexture(GL_TEXTURE_2D, t->tex_id); canvas->draw_generic_textured_rect(screenrect, Rect2(0, 0, 1, 1)); glBindTexture(GL_TEXTURE_2D, 0); @@ -344,28 +335,7 @@ void RasterizerGLES2::set_boot_image(const Ref<Image> &p_image, const Color &p_c storage->free(texture); - if (OS::get_singleton()->is_layered_allowed()) { - if (OS::get_singleton()->get_window_per_pixel_transparency_enabled()) { -#if (defined WINDOWS_ENABLED) && !(defined UWP_ENABLED) - Size2 wndsize = OS::get_singleton()->get_layered_buffer_size(); - uint8_t *data = OS::get_singleton()->get_layered_buffer_data(); - if (data) { - glReadPixels(0, 0, wndsize.x, wndsize.y, GL_BGRA, GL_UNSIGNED_BYTE, data); - OS::get_singleton()->swap_layered_buffer(); - - return; - } -#endif - } else { - //clear alpha - glColorMask(false, false, false, true); - glClearColor(0, 0, 0, 1); - glClear(GL_COLOR_BUFFER_BIT); - glColorMask(true, true, true, true); - } - } - - OS::get_singleton()->swap_buffers(); + end_frame(true); } void RasterizerGLES2::blit_render_target_to_screen(RID p_render_target, const Rect2 &p_screen_rect, int p_screen) { @@ -378,21 +348,19 @@ void RasterizerGLES2::blit_render_target_to_screen(RID p_render_target, const Re canvas->state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_TEXTURE_RECT, true); canvas->state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_UV_ATTRIBUTE, false); + canvas->state.canvas_shader.set_custom_shader(0); canvas->state.canvas_shader.bind(); canvas->canvas_begin(); - canvas->state.canvas_shader.set_uniform(CanvasShaderGLES2::BLIT_PASS, true); glDisable(GL_BLEND); glBindFramebuffer(GL_FRAMEBUFFER, RasterizerStorageGLES2::system_fbo); - glActiveTexture(GL_TEXTURE0); + glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 1); glBindTexture(GL_TEXTURE_2D, rt->color); // TODO normals canvas->draw_generic_textured_rect(p_screen_rect, Rect2(0, 0, 1, -1)); - canvas->state.canvas_shader.set_uniform(CanvasShaderGLES2::BLIT_PASS, false); - glBindTexture(GL_TEXTURE_2D, 0); canvas->canvas_end(); } @@ -452,7 +420,6 @@ RasterizerGLES2::RasterizerGLES2() { scene->storage = storage; storage->scene = scene; - prev_ticks = 0; time_total = 0; } diff --git a/drivers/gles2/rasterizer_gles2.h b/drivers/gles2/rasterizer_gles2.h index 8d57275449..f727af39dd 100644 --- a/drivers/gles2/rasterizer_gles2.h +++ b/drivers/gles2/rasterizer_gles2.h @@ -43,7 +43,6 @@ class RasterizerGLES2 : public Rasterizer { RasterizerCanvasGLES2 *canvas; RasterizerSceneGLES2 *scene; - uint64_t prev_ticks; double time_total; public: @@ -54,7 +53,7 @@ public: virtual void set_boot_image(const Ref<Image> &p_image, const Color &p_color, bool p_scale); virtual void initialize(); - virtual void begin_frame(); + virtual void begin_frame(double frame_step); virtual void set_current_render_target(RID p_render_target); virtual void restore_render_target(); virtual void clear_render_target(const Color &p_color); diff --git a/drivers/gles2/rasterizer_scene_gles2.cpp b/drivers/gles2/rasterizer_scene_gles2.cpp index f7712be5d0..1655f55bfa 100644 --- a/drivers/gles2/rasterizer_scene_gles2.cpp +++ b/drivers/gles2/rasterizer_scene_gles2.cpp @@ -28,38 +28,398 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ #include "rasterizer_scene_gles2.h" +#include "math/transform.h" #include "math_funcs.h" #include "os/os.h" #include "project_settings.h" #include "rasterizer_canvas_gles2.h" #include "servers/visual/visual_server_raster.h" +#include "vmap.h" + #ifndef GLES_OVER_GL #define glClearDepth glClearDepthf #endif +static const GLenum _cube_side_enum[6] = { + + GL_TEXTURE_CUBE_MAP_NEGATIVE_X, + GL_TEXTURE_CUBE_MAP_POSITIVE_X, + GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, + GL_TEXTURE_CUBE_MAP_POSITIVE_Y, + GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, + GL_TEXTURE_CUBE_MAP_POSITIVE_Z, + +}; + /* SHADOW ATLAS API */ RID RasterizerSceneGLES2::shadow_atlas_create() { - return RID(); + ShadowAtlas *shadow_atlas = memnew(ShadowAtlas); + shadow_atlas->fbo = 0; + shadow_atlas->depth = 0; + shadow_atlas->size = 0; + shadow_atlas->smallest_subdiv = 0; + + for (int i = 0; i < 4; i++) { + shadow_atlas->size_order[i] = i; + } + + return shadow_atlas_owner.make_rid(shadow_atlas); } void RasterizerSceneGLES2::shadow_atlas_set_size(RID p_atlas, int p_size) { + ShadowAtlas *shadow_atlas = shadow_atlas_owner.getornull(p_atlas); + ERR_FAIL_COND(!shadow_atlas); + ERR_FAIL_COND(p_size < 0); + + p_size = next_power_of_2(p_size); + + if (p_size == shadow_atlas->size) + return; + + // erase the old atlast + if (shadow_atlas->fbo) { + glDeleteTextures(1, &shadow_atlas->depth); + glDeleteFramebuffers(1, &shadow_atlas->fbo); + + shadow_atlas->fbo = 0; + shadow_atlas->depth = 0; + } + + // erase shadow atlast references from lights + for (Map<RID, uint32_t>::Element *E = shadow_atlas->shadow_owners.front(); E; E = E->next()) { + LightInstance *li = light_instance_owner.getornull(E->key()); + ERR_CONTINUE(!li); + li->shadow_atlases.erase(p_atlas); + } + + shadow_atlas->shadow_owners.clear(); + + shadow_atlas->size = p_size; + + if (shadow_atlas->size) { + glGenFramebuffers(1, &shadow_atlas->fbo); + glBindFramebuffer(GL_FRAMEBUFFER, shadow_atlas->fbo); + + // create a depth texture + glActiveTexture(GL_TEXTURE0); + glGenTextures(1, &shadow_atlas->depth); + glBindTexture(GL_TEXTURE_2D, shadow_atlas->depth); + glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT16, shadow_atlas->size, shadow_atlas->size, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, shadow_atlas->depth, 0); + + glViewport(0, 0, shadow_atlas->size, shadow_atlas->size); + + glDepthMask(GL_TRUE); + + glClearDepth(0.0f); + glClear(GL_DEPTH_BUFFER_BIT); + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + } } void RasterizerSceneGLES2::shadow_atlas_set_quadrant_subdivision(RID p_atlas, int p_quadrant, int p_subdivision) { + ShadowAtlas *shadow_atlas = shadow_atlas_owner.getornull(p_atlas); + ERR_FAIL_COND(!shadow_atlas); + ERR_FAIL_INDEX(p_quadrant, 4); + ERR_FAIL_INDEX(p_subdivision, 16384); + + uint32_t subdiv = next_power_of_2(p_subdivision); + if (subdiv & 0xaaaaaaaa) { // sqrt(subdiv) must be integer + subdiv <<= 1; + } + + subdiv = int(Math::sqrt((float)subdiv)); + + if (shadow_atlas->quadrants[p_quadrant].shadows.size() == subdiv) + return; + + // erase all data from the quadrant + for (int i = 0; i < shadow_atlas->quadrants[p_quadrant].shadows.size(); i++) { + if (shadow_atlas->quadrants[p_quadrant].shadows[i].owner.is_valid()) { + shadow_atlas->shadow_owners.erase(shadow_atlas->quadrants[p_quadrant].shadows[i].owner); + + LightInstance *li = light_instance_owner.getornull(shadow_atlas->quadrants[p_quadrant].shadows[i].owner); + ERR_CONTINUE(!li); + li->shadow_atlases.erase(p_atlas); + } + } + + shadow_atlas->quadrants[p_quadrant].shadows.resize(0); + shadow_atlas->quadrants[p_quadrant].shadows.resize(subdiv); + shadow_atlas->quadrants[p_quadrant].subdivision = subdiv; + + // cache the smallest subdivision for faster allocations + + shadow_atlas->smallest_subdiv = 1 << 30; + + for (int i = 0; i < 4; i++) { + if (shadow_atlas->quadrants[i].subdivision) { + shadow_atlas->smallest_subdiv = MIN(shadow_atlas->smallest_subdiv, shadow_atlas->quadrants[i].subdivision); + } + } + + if (shadow_atlas->smallest_subdiv == 1 << 30) { + shadow_atlas->smallest_subdiv = 0; + } + + // re-sort the quadrants + + int swaps = 0; + do { + swaps = 0; + + for (int i = 0; i < 3; i++) { + if (shadow_atlas->quadrants[shadow_atlas->size_order[i]].subdivision < shadow_atlas->quadrants[shadow_atlas->size_order[i + 1]].subdivision) { + SWAP(shadow_atlas->size_order[i], shadow_atlas->size_order[i + 1]); + swaps++; + } + } + + } while (swaps > 0); +} + +bool RasterizerSceneGLES2::_shadow_atlas_find_shadow(ShadowAtlas *shadow_atlas, int *p_in_quadrants, int p_quadrant_count, int p_current_subdiv, uint64_t p_tick, int &r_quadrant, int &r_shadow) { + + for (int i = p_quadrant_count - 1; i >= 0; i--) { + int qidx = p_in_quadrants[i]; + + if (shadow_atlas->quadrants[qidx].subdivision == (uint32_t)p_current_subdiv) { + return false; + } + + // look for an empty space + + int sc = shadow_atlas->quadrants[qidx].shadows.size(); + + ShadowAtlas::Quadrant::Shadow *sarr = shadow_atlas->quadrants[qidx].shadows.ptrw(); + + int found_free_idx = -1; // found a free one + int found_used_idx = -1; // found an existing one, must steal it + uint64_t min_pass = 0; // pass of the existing one, try to use the least recently + + for (int j = 0; j < sc; j++) { + if (!sarr[j].owner.is_valid()) { + found_free_idx = j; + break; + } + + LightInstance *sli = light_instance_owner.getornull(sarr[j].owner); + ERR_CONTINUE(!sli); + + if (sli->last_scene_pass != scene_pass) { + + // was just allocated, don't kill it so soon, wait a bit... + + if (p_tick - sarr[j].alloc_tick < shadow_atlas_realloc_tolerance_msec) { + continue; + } + + if (found_used_idx == -1 || sli->last_scene_pass < min_pass) { + found_used_idx = j; + min_pass = sli->last_scene_pass; + } + } + } + + if (found_free_idx == -1 && found_used_idx == -1) { + continue; // nothing found + } + + if (found_free_idx == -1 && found_used_idx != -1) { + found_free_idx = found_used_idx; + } + + r_quadrant = qidx; + r_shadow = found_free_idx; + + return true; + } + + return false; } bool RasterizerSceneGLES2::shadow_atlas_update_light(RID p_atlas, RID p_light_intance, float p_coverage, uint64_t p_light_version) { + + ShadowAtlas *shadow_atlas = shadow_atlas_owner.getornull(p_atlas); + ERR_FAIL_COND_V(!shadow_atlas, false); + + LightInstance *li = light_instance_owner.getornull(p_light_intance); + ERR_FAIL_COND_V(!li, false); + + if (shadow_atlas->size == 0 || shadow_atlas->smallest_subdiv == 0) { + return false; + } + + uint32_t quad_size = shadow_atlas->size >> 1; + int desired_fit = MIN(quad_size / shadow_atlas->smallest_subdiv, next_power_of_2(quad_size * p_coverage)); + + int valid_quadrants[4]; + int valid_quadrant_count = 0; + int best_size = -1; + int best_subdiv = -1; + + for (int i = 0; i < 4; i++) { + int q = shadow_atlas->size_order[i]; + int sd = shadow_atlas->quadrants[q].subdivision; + + if (sd == 0) { + continue; + } + + int max_fit = quad_size / sd; + + if (best_size != -1 && max_fit > best_size) { + break; // what we asked for is bigger than this. + } + + valid_quadrants[valid_quadrant_count] = q; + valid_quadrant_count++; + + best_subdiv = sd; + + if (max_fit >= desired_fit) { + best_size = max_fit; + } + } + + ERR_FAIL_COND_V(valid_quadrant_count == 0, false); // no suitable block available + + uint64_t tick = OS::get_singleton()->get_ticks_msec(); + + if (shadow_atlas->shadow_owners.has(p_light_intance)) { + // light was already known! + + uint32_t key = shadow_atlas->shadow_owners[p_light_intance]; + uint32_t q = (key >> ShadowAtlas::QUADRANT_SHIFT) & 0x3; + uint32_t s = key & ShadowAtlas::SHADOW_INDEX_MASK; + + bool should_realloc = shadow_atlas->quadrants[q].subdivision != (uint32_t)best_subdiv && (shadow_atlas->quadrants[q].shadows[s].alloc_tick - tick > shadow_atlas_realloc_tolerance_msec); + + bool should_redraw = shadow_atlas->quadrants[q].shadows[s].version != p_light_version; + + if (!should_realloc) { + shadow_atlas->quadrants[q].shadows.write[s].version = p_light_version; + return should_redraw; + } + + int new_quadrant; + int new_shadow; + + // find a better place + + if (_shadow_atlas_find_shadow(shadow_atlas, valid_quadrants, valid_quadrant_count, shadow_atlas->quadrants[q].subdivision, tick, new_quadrant, new_shadow)) { + // found a better place + + ShadowAtlas::Quadrant::Shadow *sh = &shadow_atlas->quadrants[new_quadrant].shadows.write[new_shadow]; + if (sh->owner.is_valid()) { + // it is take but invalid, so we can take it + + shadow_atlas->shadow_owners.erase(sh->owner); + LightInstance *sli = light_instance_owner.get(sh->owner); + sli->shadow_atlases.erase(p_atlas); + } + + // erase previous + shadow_atlas->quadrants[q].shadows.write[s].version = 0; + shadow_atlas->quadrants[q].shadows.write[s].owner = RID(); + + sh->owner = p_light_intance; + sh->alloc_tick = tick; + sh->version = p_light_version; + li->shadow_atlases.insert(p_atlas); + + // make a new key + key = new_quadrant << ShadowAtlas::QUADRANT_SHIFT; + key |= new_shadow; + + // update it in the map + shadow_atlas->shadow_owners[p_light_intance] = key; + + // make it dirty, so we redraw + return true; + } + + // no better place found, so we keep the current place + + shadow_atlas->quadrants[q].shadows.write[s].version = p_light_version; + + return should_redraw; + } + + int new_quadrant; + int new_shadow; + + if (_shadow_atlas_find_shadow(shadow_atlas, valid_quadrants, valid_quadrant_count, -1, tick, new_quadrant, new_shadow)) { + // found a better place + + ShadowAtlas::Quadrant::Shadow *sh = &shadow_atlas->quadrants[new_quadrant].shadows.write[new_shadow]; + if (sh->owner.is_valid()) { + // it is take but invalid, so we can take it + + shadow_atlas->shadow_owners.erase(sh->owner); + LightInstance *sli = light_instance_owner.get(sh->owner); + sli->shadow_atlases.erase(p_atlas); + } + + sh->owner = p_light_intance; + sh->alloc_tick = tick; + sh->version = p_light_version; + li->shadow_atlases.insert(p_atlas); + + // make a new key + uint32_t key = new_quadrant << ShadowAtlas::QUADRANT_SHIFT; + key |= new_shadow; + + // update it in the map + shadow_atlas->shadow_owners[p_light_intance] = key; + + // make it dirty, so we redraw + return true; + } + return false; } void RasterizerSceneGLES2::set_directional_shadow_count(int p_count) { + directional_shadow.light_count = p_count; + directional_shadow.current_light = 0; } int RasterizerSceneGLES2::get_directional_light_shadow_size(RID p_light_intance) { - return 0; + + ERR_FAIL_COND_V(directional_shadow.light_count == 0, 0); + + int shadow_size; + + if (directional_shadow.light_count == 1) { + shadow_size = directional_shadow.size; + } else { + shadow_size = directional_shadow.size / 2; //more than 4 not supported anyway + } + + LightInstance *light_instance = light_instance_owner.getornull(p_light_intance); + ERR_FAIL_COND_V(!light_instance, 0); + + switch (light_instance->light_ptr->directional_shadow_mode) { + case VS::LIGHT_DIRECTIONAL_SHADOW_ORTHOGONAL: + break; //none + case VS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_2_SPLITS: + case VS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_4_SPLITS: + shadow_size /= 2; + break; + } + + return shadow_size; } ////////////////////////////////////////////////////// @@ -105,86 +465,183 @@ bool RasterizerSceneGLES2::reflection_probe_instance_postprocess_step(RID p_inst RID RasterizerSceneGLES2::environment_create() { - return RID(); + Environment *env = memnew(Environment); + + return environment_owner.make_rid(env); } void RasterizerSceneGLES2::environment_set_background(RID p_env, VS::EnvironmentBG p_bg) { + + Environment *env = environment_owner.getornull(p_env); + ERR_FAIL_COND(!env); + env->bg_mode = p_bg; } void RasterizerSceneGLES2::environment_set_sky(RID p_env, RID p_sky) { + Environment *env = environment_owner.getornull(p_env); + ERR_FAIL_COND(!env); + + env->sky = p_sky; } void RasterizerSceneGLES2::environment_set_sky_custom_fov(RID p_env, float p_scale) { + Environment *env = environment_owner.getornull(p_env); + ERR_FAIL_COND(!env); + + env->sky_custom_fov = p_scale; } void RasterizerSceneGLES2::environment_set_bg_color(RID p_env, const Color &p_color) { + Environment *env = environment_owner.getornull(p_env); + ERR_FAIL_COND(!env); + + env->bg_color = p_color; } void RasterizerSceneGLES2::environment_set_bg_energy(RID p_env, float p_energy) { + Environment *env = environment_owner.getornull(p_env); + ERR_FAIL_COND(!env); + + env->bg_energy = p_energy; } void RasterizerSceneGLES2::environment_set_canvas_max_layer(RID p_env, int p_max_layer) { + Environment *env = environment_owner.getornull(p_env); + ERR_FAIL_COND(!env); + + env->canvas_max_layer = p_max_layer; } void RasterizerSceneGLES2::environment_set_ambient_light(RID p_env, const Color &p_color, float p_energy, float p_sky_contribution) { + Environment *env = environment_owner.getornull(p_env); + ERR_FAIL_COND(!env); + + env->ambient_color = p_color; + env->ambient_energy = p_energy; + env->ambient_sky_contribution = p_sky_contribution; } void RasterizerSceneGLES2::environment_set_dof_blur_far(RID p_env, bool p_enable, float p_distance, float p_transition, float p_amount, VS::EnvironmentDOFBlurQuality p_quality) { + Environment *env = environment_owner.getornull(p_env); + ERR_FAIL_COND(!env); } void RasterizerSceneGLES2::environment_set_dof_blur_near(RID p_env, bool p_enable, float p_distance, float p_transition, float p_amount, VS::EnvironmentDOFBlurQuality p_quality) { + Environment *env = environment_owner.getornull(p_env); + ERR_FAIL_COND(!env); } void RasterizerSceneGLES2::environment_set_glow(RID p_env, bool p_enable, int p_level_flags, float p_intensity, float p_strength, float p_bloom_threshold, VS::EnvironmentGlowBlendMode p_blend_mode, float p_hdr_bleed_threshold, float p_hdr_bleed_scale, bool p_bicubic_upscale) { + Environment *env = environment_owner.getornull(p_env); + ERR_FAIL_COND(!env); } void RasterizerSceneGLES2::environment_set_fog(RID p_env, bool p_enable, float p_begin, float p_end, RID p_gradient_texture) { + Environment *env = environment_owner.getornull(p_env); + ERR_FAIL_COND(!env); } void RasterizerSceneGLES2::environment_set_ssr(RID p_env, bool p_enable, int p_max_steps, float p_fade_in, float p_fade_out, float p_depth_tolerance, bool p_roughness) { + Environment *env = environment_owner.getornull(p_env); + ERR_FAIL_COND(!env); } void RasterizerSceneGLES2::environment_set_ssao(RID p_env, bool p_enable, float p_radius, float p_intensity, float p_radius2, float p_intensity2, float p_bias, float p_light_affect, float p_ao_channel_affect, const Color &p_color, VS::EnvironmentSSAOQuality p_quality, VisualServer::EnvironmentSSAOBlur p_blur, float p_bilateral_sharpness) { + Environment *env = environment_owner.getornull(p_env); + ERR_FAIL_COND(!env); } void RasterizerSceneGLES2::environment_set_tonemap(RID p_env, VS::EnvironmentToneMapper p_tone_mapper, float p_exposure, float p_white, bool p_auto_exposure, float p_min_luminance, float p_max_luminance, float p_auto_exp_speed, float p_auto_exp_scale) { + Environment *env = environment_owner.getornull(p_env); + ERR_FAIL_COND(!env); } void RasterizerSceneGLES2::environment_set_adjustment(RID p_env, bool p_enable, float p_brightness, float p_contrast, float p_saturation, RID p_ramp) { + Environment *env = environment_owner.getornull(p_env); + ERR_FAIL_COND(!env); } void RasterizerSceneGLES2::environment_set_fog(RID p_env, bool p_enable, const Color &p_color, const Color &p_sun_color, float p_sun_amount) { + Environment *env = environment_owner.getornull(p_env); + ERR_FAIL_COND(!env); } void RasterizerSceneGLES2::environment_set_fog_depth(RID p_env, bool p_enable, float p_depth_begin, float p_depth_curve, bool p_transmit, float p_transmit_curve) { + Environment *env = environment_owner.getornull(p_env); + ERR_FAIL_COND(!env); } void RasterizerSceneGLES2::environment_set_fog_height(RID p_env, bool p_enable, float p_min_height, float p_max_height, float p_height_curve) { + Environment *env = environment_owner.getornull(p_env); + ERR_FAIL_COND(!env); } bool RasterizerSceneGLES2::is_environment(RID p_env) { - return false; + return environment_owner.owns(p_env); } VS::EnvironmentBG RasterizerSceneGLES2::environment_get_background(RID p_env) { - return VS::ENV_BG_CLEAR_COLOR; + const Environment *env = environment_owner.getornull(p_env); + ERR_FAIL_COND_V(!env, VS::ENV_BG_MAX); + + return env->bg_mode; } int RasterizerSceneGLES2::environment_get_canvas_max_layer(RID p_env) { - return 0; + const Environment *env = environment_owner.getornull(p_env); + ERR_FAIL_COND_V(!env, -1); + + return env->canvas_max_layer; } RID RasterizerSceneGLES2::light_instance_create(RID p_light) { - return RID(); + + LightInstance *light_instance = memnew(LightInstance); + + light_instance->last_scene_pass = 0; + + light_instance->light = p_light; + light_instance->light_ptr = storage->light_owner.getornull(p_light); + + ERR_FAIL_COND_V(!light_instance->light_ptr, RID()); + + light_instance->self = light_instance_owner.make_rid(light_instance); + + return light_instance->self; } void RasterizerSceneGLES2::light_instance_set_transform(RID p_light_instance, const Transform &p_transform) { + + LightInstance *light_instance = light_instance_owner.getornull(p_light_instance); + ERR_FAIL_COND(!light_instance); + + light_instance->transform = p_transform; } void RasterizerSceneGLES2::light_instance_set_shadow_transform(RID p_light_instance, const CameraMatrix &p_projection, const Transform &p_transform, float p_far, float p_split, int p_pass, float p_bias_scale) { + + LightInstance *light_instance = light_instance_owner.getornull(p_light_instance); + ERR_FAIL_COND(!light_instance); + + if (light_instance->light_ptr->type != VS::LIGHT_DIRECTIONAL) { + p_pass = 0; + } + + ERR_FAIL_INDEX(p_pass, 4); + + light_instance->shadow_transform[p_pass].camera = p_projection; + light_instance->shadow_transform[p_pass].transform = p_transform; + light_instance->shadow_transform[p_pass].farplane = p_far; + light_instance->shadow_transform[p_pass].split = p_split; + light_instance->shadow_transform[p_pass].bias_scale = p_bias_scale; } void RasterizerSceneGLES2::light_instance_mark_visible(RID p_light_instance) { + + LightInstance *light_instance = light_instance_owner.getornull(p_light_instance); + ERR_FAIL_COND(!light_instance); + + light_instance->last_scene_pass = scene_pass; } ////////////////////// @@ -206,13 +663,1693 @@ void RasterizerSceneGLES2::gi_probe_instance_set_bounds(RID p_probe, const Vecto //////////////////////////// //////////////////////////// +void RasterizerSceneGLES2::_add_geometry(RasterizerStorageGLES2::Geometry *p_geometry, InstanceBase *p_instance, RasterizerStorageGLES2::GeometryOwner *p_owner, int p_material, bool p_depth_pass, bool p_shadow_pass) { + + RasterizerStorageGLES2::Material *material = NULL; + RID material_src; + + if (p_instance->material_override.is_valid()) { + material_src = p_instance->material_override; + } else if (p_material >= 0) { + material_src = p_instance->materials[p_material]; + } else { + material_src = p_geometry->material; + } + + if (material_src.is_valid()) { + material = storage->material_owner.getornull(material_src); + + if (!material->shader || !material->shader->valid) { + material = NULL; + } + } + + if (!material) { + material = storage->material_owner.getptr(default_material); + } + + ERR_FAIL_COND(!material); + + _add_geometry_with_material(p_geometry, p_instance, p_owner, material, p_depth_pass, p_shadow_pass); + + while (material->next_pass.is_valid()) { + material = storage->material_owner.getornull(material->next_pass); + + if (!material || !material->shader || !material->shader->valid) { + break; + } + + _add_geometry_with_material(p_geometry, p_instance, p_owner, material, p_depth_pass, p_shadow_pass); + } +} +void RasterizerSceneGLES2::_add_geometry_with_material(RasterizerStorageGLES2::Geometry *p_geometry, InstanceBase *p_instance, RasterizerStorageGLES2::GeometryOwner *p_owner, RasterizerStorageGLES2::Material *p_material, bool p_depth_pass, bool p_shadow_pass) { + + bool has_base_alpha = (p_material->shader->spatial.uses_alpha && !p_material->shader->spatial.uses_alpha_scissor) || p_material->shader->spatial.uses_screen_texture || p_material->shader->spatial.uses_depth_texture; + bool has_blend_alpha = p_material->shader->spatial.blend_mode != RasterizerStorageGLES2::Shader::Spatial::BLEND_MODE_MIX; + bool has_alpha = has_base_alpha || has_blend_alpha; + + // TODO add this stuff + // bool mirror = p_instance->mirror; + // bool no_cull = false; + + RenderList::Element *e = has_alpha ? render_list.add_alpha_element() : render_list.add_element(); + + if (!e) { + return; + } + + e->geometry = p_geometry; + e->material = p_material; + e->instance = p_instance; + e->owner = p_owner; + e->sort_key = 0; + + // TODO check render pass of geometry + + // TODO check directional light flag + + if (p_depth_pass) { + // if we are in the depth pass we can sort out a few things to improve performance + + if (has_blend_alpha || p_material->shader->spatial.uses_depth_texture || (has_base_alpha && p_material->shader->spatial.depth_draw_mode != RasterizerStorageGLES2::Shader::Spatial::DEPTH_DRAW_ALPHA_PREPASS)) { + return; + } + + if (p_material->shader->spatial.uses_alpha_scissor && !p_material->shader->spatial.writes_modelview_or_projection && !p_material->shader->spatial.uses_vertex && !p_material->shader->spatial.uses_discard && p_material->shader->spatial.depth_draw_mode != RasterizerStorageGLES2::Shader::Spatial::DEPTH_DRAW_ALPHA_PREPASS) { + + // shader doesn't use discard or writes a custom vertex position, + // so we can use a stripped down shader instead + + // TODO twosided and worldcoord stuff + + p_material = storage->material_owner.getptr(default_material_twosided); + } + + has_alpha = false; + } + + e->sort_key |= uint64_t(e->geometry->index) << RenderList::SORT_KEY_GEOMETRY_INDEX_SHIFT; + e->sort_key |= uint64_t(e->instance->base_type) << RenderList::SORT_KEY_GEOMETRY_TYPE_SHIFT; + + if (p_material->shader->spatial.unshaded) { + e->sort_key |= SORT_KEY_UNSHADED_FLAG; + } + + if (!p_depth_pass) { + e->sort_key |= uint64_t(e->material->index) << RenderList::SORT_KEY_MATERIAL_INDEX_SHIFT; + + e->sort_key |= uint64_t(p_material->render_priority + 128) << RenderList::SORT_KEY_PRIORITY_SHIFT; + } else { + // TODO + } + + if (p_material->shader->spatial.uses_time) { + VisualServerRaster::redraw_request(); + } +} + +void RasterizerSceneGLES2::_fill_render_list(InstanceBase **p_cull_result, int p_cull_count, bool p_depth_pass, bool p_shadow_pass) { + + for (int i = 0; i < p_cull_count; i++) { + + InstanceBase *instance = p_cull_result[i]; + + switch (instance->base_type) { + + case VS::INSTANCE_MESH: { + + RasterizerStorageGLES2::Mesh *mesh = storage->mesh_owner.getornull(instance->base); + ERR_CONTINUE(!mesh); + + int num_surfaces = mesh->surfaces.size(); + + for (int i = 0; i < num_surfaces; i++) { + int material_index = instance->materials[i].is_valid() ? i : -1; + + RasterizerStorageGLES2::Surface *surface = mesh->surfaces[i]; + + _add_geometry(surface, instance, NULL, material_index, p_depth_pass, p_shadow_pass); + } + + } break; + + case VS::INSTANCE_MULTIMESH: { + RasterizerStorageGLES2::MultiMesh *multi_mesh = storage->multimesh_owner.getptr(instance->base); + ERR_CONTINUE(!multi_mesh); + + if (multi_mesh->size == 0 || multi_mesh->visible_instances == 0) + continue; + + RasterizerStorageGLES2::Mesh *mesh = storage->mesh_owner.getptr(multi_mesh->mesh); + if (!mesh) + continue; + + int ssize = mesh->surfaces.size(); + + for (int i = 0; i < ssize; i++) { + RasterizerStorageGLES2::Surface *s = mesh->surfaces[i]; + _add_geometry(s, instance, multi_mesh, -1, p_depth_pass, p_shadow_pass); + } + } break; + + case VS::INSTANCE_IMMEDIATE: { + RasterizerStorageGLES2::Immediate *im = storage->immediate_owner.getptr(instance->base); + ERR_CONTINUE(!im); + + _add_geometry(im, instance, NULL, -1, p_depth_pass, p_shadow_pass); + + } break; + + default: { + + } break; + } + } +} + +static const GLenum gl_primitive[] = { + GL_POINTS, + GL_LINES, + GL_LINE_STRIP, + GL_LINE_LOOP, + GL_TRIANGLES, + GL_TRIANGLE_STRIP, + GL_TRIANGLE_FAN +}; + +void RasterizerSceneGLES2::_setup_material(RasterizerStorageGLES2::Material *p_material, bool p_reverse_cull, bool p_alpha_pass, Size2i p_skeleton_tex_size) { + + // material parameters + + state.scene_shader.set_custom_shader(p_material->shader->custom_code_id); + + state.scene_shader.bind(); + + if (p_material->shader->spatial.no_depth_test) { + glDisable(GL_DEPTH_TEST); + } else { + glEnable(GL_DEPTH_TEST); + } + + switch (p_material->shader->spatial.depth_draw_mode) { + case RasterizerStorageGLES2::Shader::Spatial::DEPTH_DRAW_ALPHA_PREPASS: + case RasterizerStorageGLES2::Shader::Spatial::DEPTH_DRAW_OPAQUE: { + + glDepthMask(!p_alpha_pass); + } break; + case RasterizerStorageGLES2::Shader::Spatial::DEPTH_DRAW_ALWAYS: { + glDepthMask(GL_TRUE); + } break; + case RasterizerStorageGLES2::Shader::Spatial::DEPTH_DRAW_NEVER: { + glDepthMask(GL_FALSE); + } break; + } + + // TODO whyyyyy???? + p_reverse_cull = true; + + switch (p_material->shader->spatial.cull_mode) { + case RasterizerStorageGLES2::Shader::Spatial::CULL_MODE_DISABLED: { + glDisable(GL_CULL_FACE); + } break; + + case RasterizerStorageGLES2::Shader::Spatial::CULL_MODE_BACK: { + glEnable(GL_CULL_FACE); + glCullFace(p_reverse_cull ? GL_FRONT : GL_BACK); + } break; + case RasterizerStorageGLES2::Shader::Spatial::CULL_MODE_FRONT: { + glEnable(GL_CULL_FACE); + glCullFace(p_reverse_cull ? GL_BACK : GL_FRONT); + } break; + } + + int tc = p_material->textures.size(); + Pair<StringName, RID> *textures = p_material->textures.ptrw(); + + ShaderLanguage::ShaderNode::Uniform::Hint *texture_hints = p_material->shader->texture_hints.ptrw(); + + state.scene_shader.set_uniform(SceneShaderGLES2::SKELETON_TEXTURE_SIZE, p_skeleton_tex_size); + + for (int i = 0; i < tc; i++) { + + glActiveTexture(GL_TEXTURE0 + i); + + RasterizerStorageGLES2::Texture *t = storage->texture_owner.getornull(textures[i].second); + + if (!t) { + + switch (texture_hints[i]) { + case ShaderLanguage::ShaderNode::Uniform::HINT_BLACK_ALBEDO: + case ShaderLanguage::ShaderNode::Uniform::HINT_BLACK: { + glBindTexture(GL_TEXTURE_2D, storage->resources.black_tex); + } break; + case ShaderLanguage::ShaderNode::Uniform::HINT_ANISO: { + glBindTexture(GL_TEXTURE_2D, storage->resources.aniso_tex); + } break; + case ShaderLanguage::ShaderNode::Uniform::HINT_NORMAL: { + glBindTexture(GL_TEXTURE_2D, storage->resources.normal_tex); + } break; + default: { + glBindTexture(GL_TEXTURE_2D, storage->resources.white_tex); + } break; + } + + continue; + } + + t = t->get_ptr(); + + glBindTexture(t->target, t->tex_id); + } + state.scene_shader.use_material((void *)p_material); +} + +void RasterizerSceneGLES2::_setup_geometry(RenderList::Element *p_element, RasterizerStorageGLES2::Skeleton *p_skeleton) { + + state.scene_shader.set_conditional(SceneShaderGLES2::USE_SKELETON, p_skeleton != NULL); + state.scene_shader.set_conditional(SceneShaderGLES2::USE_SKELETON_SOFTWARE, !storage->config.float_texture_supported); + // state.scene_shader.set_conditional(SceneShaderGLES2::USE_SKELETON_SOFTWARE, true); + + switch (p_element->instance->base_type) { + + case VS::INSTANCE_MESH: { + RasterizerStorageGLES2::Surface *s = static_cast<RasterizerStorageGLES2::Surface *>(p_element->geometry); + + state.scene_shader.set_conditional(SceneShaderGLES2::USE_INSTANCING, false); + state.scene_shader.set_conditional(SceneShaderGLES2::ENABLE_COLOR_INTERP, s->attribs[VS::ARRAY_COLOR].enabled); + state.scene_shader.set_conditional(SceneShaderGLES2::ENABLE_UV_INTERP, s->attribs[VS::ARRAY_TEX_UV].enabled); + state.scene_shader.set_conditional(SceneShaderGLES2::ENABLE_UV2_INTERP, s->attribs[VS::ARRAY_TEX_UV2].enabled); + + } break; + + case VS::INSTANCE_MULTIMESH: { + RasterizerStorageGLES2::MultiMesh *multi_mesh = static_cast<RasterizerStorageGLES2::MultiMesh *>(p_element->owner); + RasterizerStorageGLES2::Surface *s = static_cast<RasterizerStorageGLES2::Surface *>(p_element->geometry); + + state.scene_shader.set_conditional(SceneShaderGLES2::ENABLE_COLOR_INTERP, true); + state.scene_shader.set_conditional(SceneShaderGLES2::USE_INSTANCING, true); + + state.scene_shader.set_conditional(SceneShaderGLES2::ENABLE_UV_INTERP, s->attribs[VS::ARRAY_TEX_UV].enabled); + state.scene_shader.set_conditional(SceneShaderGLES2::ENABLE_UV2_INTERP, s->attribs[VS::ARRAY_TEX_UV2].enabled); + } break; + + case VS::INSTANCE_IMMEDIATE: { + state.scene_shader.set_conditional(SceneShaderGLES2::USE_INSTANCING, false); + state.scene_shader.set_conditional(SceneShaderGLES2::ENABLE_COLOR_INTERP, true); + state.scene_shader.set_conditional(SceneShaderGLES2::ENABLE_UV_INTERP, true); + state.scene_shader.set_conditional(SceneShaderGLES2::ENABLE_UV2_INTERP, true); + } break; + + default: { + + } break; + } + + if (storage->config.float_texture_supported) { + if (p_skeleton) { + glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 1); + glBindTexture(GL_TEXTURE_2D, p_skeleton->tex_id); + } + + return; + } + + if (p_skeleton) { + ERR_FAIL_COND(p_skeleton->use_2d); + + PoolVector<float> &transform_buffer = storage->resources.skeleton_transform_cpu_buffer; + + switch (p_element->instance->base_type) { + case VS::INSTANCE_MESH: { + RasterizerStorageGLES2::Surface *s = static_cast<RasterizerStorageGLES2::Surface *>(p_element->geometry); + + if (!s->attribs[VS::ARRAY_BONES].enabled || !s->attribs[VS::ARRAY_WEIGHTS].enabled) { + break; // the whole instance has a skeleton, but this surface is not affected by it. + } + + // 3 * vec4 per vertex + if (transform_buffer.size() < s->array_len * 12) { + transform_buffer.resize(s->array_len * 12); + } + + const size_t bones_offset = s->attribs[VS::ARRAY_BONES].offset; + const size_t bones_stride = s->attribs[VS::ARRAY_BONES].stride; + const size_t bone_weight_offset = s->attribs[VS::ARRAY_WEIGHTS].offset; + const size_t bone_weight_stride = s->attribs[VS::ARRAY_WEIGHTS].stride; + + { + PoolVector<float>::Write write = transform_buffer.write(); + float *buffer = write.ptr(); + + PoolVector<uint8_t>::Read vertex_array_read = s->data.read(); + const uint8_t *vertex_data = vertex_array_read.ptr(); + + for (int i = 0; i < s->array_len; i++) { + + // do magic + + size_t bones[4]; + float bone_weight[4]; + + if (s->attribs[VS::ARRAY_BONES].type == GL_UNSIGNED_BYTE) { + // read as byte + const uint8_t *bones_ptr = vertex_data + bones_offset + (i * bones_stride); + bones[0] = bones_ptr[0]; + bones[1] = bones_ptr[1]; + bones[2] = bones_ptr[2]; + bones[3] = bones_ptr[3]; + } else { + // read as short + const uint16_t *bones_ptr = (const uint16_t *)(vertex_data + bones_offset + (i * bones_stride)); + bones[0] = bones_ptr[0]; + bones[1] = bones_ptr[1]; + bones[2] = bones_ptr[2]; + bones[3] = bones_ptr[3]; + } + + if (s->attribs[VS::ARRAY_WEIGHTS].type == GL_FLOAT) { + // read as float + const float *weight_ptr = (const float *)(vertex_data + bone_weight_offset + (i * bone_weight_stride)); + bone_weight[0] = weight_ptr[0]; + bone_weight[1] = weight_ptr[1]; + bone_weight[2] = weight_ptr[2]; + bone_weight[3] = weight_ptr[3]; + } else { + // read as half + const uint16_t *weight_ptr = (const uint16_t *)(vertex_data + bone_weight_offset + (i * bone_weight_stride)); + bone_weight[0] = (weight_ptr[0] / (float)0xFFFF); + bone_weight[1] = (weight_ptr[1] / (float)0xFFFF); + bone_weight[2] = (weight_ptr[2] / (float)0xFFFF); + bone_weight[3] = (weight_ptr[3] / (float)0xFFFF); + } + + size_t offset = i * 12; + + Transform transform; + + Transform bone_transforms[4] = { + storage->skeleton_bone_get_transform(p_element->instance->skeleton, bones[0]), + storage->skeleton_bone_get_transform(p_element->instance->skeleton, bones[1]), + storage->skeleton_bone_get_transform(p_element->instance->skeleton, bones[2]), + storage->skeleton_bone_get_transform(p_element->instance->skeleton, bones[3]), + }; + + transform.origin = + bone_weight[0] * bone_transforms[0].origin + + bone_weight[1] * bone_transforms[1].origin + + bone_weight[2] * bone_transforms[2].origin + + bone_weight[3] * bone_transforms[3].origin; + + transform.basis = + bone_transforms[0].basis * bone_weight[0] + + bone_transforms[1].basis * bone_weight[1] + + bone_transforms[2].basis * bone_weight[2] + + bone_transforms[3].basis * bone_weight[3]; + + float row[3][4] = { + { transform.basis[0][0], transform.basis[0][1], transform.basis[0][2], transform.origin[0] }, + { transform.basis[1][0], transform.basis[1][1], transform.basis[1][2], transform.origin[1] }, + { transform.basis[2][0], transform.basis[2][1], transform.basis[2][2], transform.origin[2] }, + }; + + size_t transform_buffer_offset = i * 12; + + copymem(&buffer[transform_buffer_offset], row, sizeof(row)); + } + } + + storage->_update_skeleton_transform_buffer(transform_buffer, s->array_len * 12); + } break; + + default: { + + } break; + } + } +} + +void RasterizerSceneGLES2::_render_geometry(RenderList::Element *p_element) { + + switch (p_element->instance->base_type) { + + case VS::INSTANCE_MESH: { + + RasterizerStorageGLES2::Surface *s = static_cast<RasterizerStorageGLES2::Surface *>(p_element->geometry); + + // set up + + if (p_element->instance->skeleton.is_valid() && s->attribs[VS::ARRAY_BONES].enabled && s->attribs[VS::ARRAY_WEIGHTS].enabled) { + glBindBuffer(GL_ARRAY_BUFFER, storage->resources.skeleton_transform_buffer); + + glEnableVertexAttribArray(VS::ARRAY_MAX + 0); + glEnableVertexAttribArray(VS::ARRAY_MAX + 1); + glEnableVertexAttribArray(VS::ARRAY_MAX + 2); + + glVertexAttribPointer(VS::ARRAY_MAX + 0, 4, GL_FLOAT, GL_FALSE, sizeof(float) * 12, (const void *)(sizeof(float) * 4 * 0)); + glVertexAttribPointer(VS::ARRAY_MAX + 1, 4, GL_FLOAT, GL_FALSE, sizeof(float) * 12, (const void *)(sizeof(float) * 4 * 1)); + glVertexAttribPointer(VS::ARRAY_MAX + 2, 4, GL_FLOAT, GL_FALSE, sizeof(float) * 12, (const void *)(sizeof(float) * 4 * 2)); + } else { + // just to make sure + glDisableVertexAttribArray(VS::ARRAY_MAX + 0); + glDisableVertexAttribArray(VS::ARRAY_MAX + 1); + glDisableVertexAttribArray(VS::ARRAY_MAX + 2); + + glVertexAttrib4f(VS::ARRAY_MAX + 0, 1, 0, 0, 0); + glVertexAttrib4f(VS::ARRAY_MAX + 1, 0, 1, 0, 0); + glVertexAttrib4f(VS::ARRAY_MAX + 2, 0, 0, 1, 0); + } + + glBindBuffer(GL_ARRAY_BUFFER, s->vertex_id); + + if (s->index_array_len > 0) { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, s->index_id); + } + + for (int i = 0; i < VS::ARRAY_MAX - 1; i++) { + if (s->attribs[i].enabled) { + glEnableVertexAttribArray(i); + glVertexAttribPointer(s->attribs[i].index, s->attribs[i].size, s->attribs[i].type, s->attribs[i].normalized, s->attribs[i].stride, (uint8_t *)0 + s->attribs[i].offset); + } else { + glDisableVertexAttribArray(i); + } + } + + // drawing + + if (s->index_array_len > 0) { + glDrawElements(gl_primitive[s->primitive], s->index_array_len, (s->array_len >= (1 << 16)) ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT, 0); + } else { + glDrawArrays(gl_primitive[s->primitive], 0, s->array_len); + } + + // tear down + + for (int i = 0; i < VS::ARRAY_MAX - 1; i++) { + glDisableVertexAttribArray(i); + } + + if (s->index_array_len > 0) { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + } + + if (p_element->instance->skeleton.is_valid() && s->attribs[VS::ARRAY_BONES].enabled && s->attribs[VS::ARRAY_WEIGHTS].enabled) { + glBindBuffer(GL_ARRAY_BUFFER, storage->resources.skeleton_transform_buffer); + + glDisableVertexAttribArray(VS::ARRAY_MAX + 0); + glDisableVertexAttribArray(VS::ARRAY_MAX + 1); + glDisableVertexAttribArray(VS::ARRAY_MAX + 2); + } + + glBindBuffer(GL_ARRAY_BUFFER, 0); + + } break; + + case VS::INSTANCE_MULTIMESH: { + + RasterizerStorageGLES2::MultiMesh *multi_mesh = static_cast<RasterizerStorageGLES2::MultiMesh *>(p_element->owner); + RasterizerStorageGLES2::Surface *s = static_cast<RasterizerStorageGLES2::Surface *>(p_element->geometry); + + int amount = MIN(multi_mesh->size, multi_mesh->visible_instances); + if (amount == -1) { + amount = multi_mesh->size; + } + + if (p_element->instance->skeleton.is_valid() && s->attribs[VS::ARRAY_BONES].enabled && s->attribs[VS::ARRAY_WEIGHTS].enabled) { + glBindBuffer(GL_ARRAY_BUFFER, storage->resources.skeleton_transform_buffer); + + glEnableVertexAttribArray(VS::ARRAY_MAX + 0); + glEnableVertexAttribArray(VS::ARRAY_MAX + 1); + glEnableVertexAttribArray(VS::ARRAY_MAX + 2); + + glVertexAttribPointer(VS::ARRAY_MAX + 0, 4, GL_FLOAT, GL_FALSE, sizeof(float) * 12, (const void *)(sizeof(float) * 4 * 0)); + glVertexAttribPointer(VS::ARRAY_MAX + 1, 4, GL_FLOAT, GL_FALSE, sizeof(float) * 12, (const void *)(sizeof(float) * 4 * 1)); + glVertexAttribPointer(VS::ARRAY_MAX + 2, 4, GL_FLOAT, GL_FALSE, sizeof(float) * 12, (const void *)(sizeof(float) * 4 * 2)); + } else { + // just to make sure + glDisableVertexAttribArray(VS::ARRAY_MAX + 0); + glDisableVertexAttribArray(VS::ARRAY_MAX + 1); + glDisableVertexAttribArray(VS::ARRAY_MAX + 2); + + glVertexAttrib4f(VS::ARRAY_MAX + 0, 1, 0, 0, 0); + glVertexAttrib4f(VS::ARRAY_MAX + 1, 0, 1, 0, 0); + glVertexAttrib4f(VS::ARRAY_MAX + 2, 0, 0, 1, 0); + } + + glBindBuffer(GL_ARRAY_BUFFER, s->vertex_id); + + if (s->index_array_len > 0) { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, s->index_id); + } + + for (int i = 0; i < VS::ARRAY_MAX - 1; i++) { + if (s->attribs[i].enabled) { + glEnableVertexAttribArray(i); + glVertexAttribPointer(s->attribs[i].index, s->attribs[i].size, s->attribs[i].type, s->attribs[i].normalized, s->attribs[i].stride, (uint8_t *)0 + s->attribs[i].offset); + } else { + glDisableVertexAttribArray(i); + } + } + + glDisableVertexAttribArray(12); // transform 0 + glDisableVertexAttribArray(13); // transform 1 + glDisableVertexAttribArray(14); // transform 2 + glDisableVertexAttribArray(15); // color + glDisableVertexAttribArray(8); // custom data + + glVertexAttrib4f(15, 1, 1, 1, 1); + glVertexAttrib4f(8, 0, 0, 0, 0); + + int stride = multi_mesh->color_floats + multi_mesh->custom_data_floats + multi_mesh->xform_floats; + + int color_ofs = multi_mesh->xform_floats; + int custom_data_ofs = color_ofs + multi_mesh->color_floats; + + // drawing + + for (int i = 0; i < amount; i++) { + float *buffer = &multi_mesh->data.write[i * stride]; + + { + // inline of multimesh_get_transform since it's such a pain + // to get a RID from here... + Transform transform; + + transform.basis.elements[0][0] = buffer[0]; + transform.basis.elements[0][1] = buffer[1]; + transform.basis.elements[0][2] = buffer[2]; + transform.origin.x = buffer[3]; + transform.basis.elements[1][0] = buffer[4]; + transform.basis.elements[1][1] = buffer[5]; + transform.basis.elements[1][2] = buffer[6]; + transform.origin.y = buffer[7]; + transform.basis.elements[2][0] = buffer[8]; + transform.basis.elements[2][1] = buffer[9]; + transform.basis.elements[2][2] = buffer[10]; + transform.origin.z = buffer[11]; + + float row[3][4] = { + { transform.basis[0][0], transform.basis[0][1], transform.basis[0][2], transform.origin[0] }, + { transform.basis[1][0], transform.basis[1][1], transform.basis[1][2], transform.origin[1] }, + { transform.basis[2][0], transform.basis[2][1], transform.basis[2][2], transform.origin[2] }, + }; + + glVertexAttrib4fv(12, row[0]); + glVertexAttrib4fv(13, row[1]); + glVertexAttrib4fv(14, row[2]); + } + + if (multi_mesh->color_floats) { + glVertexAttrib4fv(15, buffer + color_ofs); + } + + if (multi_mesh->custom_data_floats) { + glVertexAttrib4fv(8, buffer + custom_data_ofs); + } + + if (s->index_array_len > 0) { + glDrawElements(gl_primitive[s->primitive], s->index_array_len, (s->array_len >= (1 << 16)) ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT, 0); + } else { + glDrawArrays(gl_primitive[s->primitive], 0, s->array_len); + } + } + + // tear down + + for (int i = 0; i < VS::ARRAY_MAX - 1; i++) { + glDisableVertexAttribArray(i); + } + + if (s->index_array_len > 0) { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + } + + if (p_element->instance->skeleton.is_valid() && s->attribs[VS::ARRAY_BONES].enabled && s->attribs[VS::ARRAY_WEIGHTS].enabled) { + glBindBuffer(GL_ARRAY_BUFFER, storage->resources.skeleton_transform_buffer); + + glDisableVertexAttribArray(VS::ARRAY_MAX + 0); + glDisableVertexAttribArray(VS::ARRAY_MAX + 1); + glDisableVertexAttribArray(VS::ARRAY_MAX + 2); + } + + glBindBuffer(GL_ARRAY_BUFFER, 0); + } break; + + case VS::INSTANCE_IMMEDIATE: { + const RasterizerStorageGLES2::Immediate *im = static_cast<const RasterizerStorageGLES2::Immediate *>(p_element->geometry); + + if (im->building) { + return; + } + + bool restore_tex = false; + + glBindBuffer(GL_ARRAY_BUFFER, state.immediate_buffer); + + for (const List<RasterizerStorageGLES2::Immediate::Chunk>::Element *E = im->chunks.front(); E; E = E->next()) { + const RasterizerStorageGLES2::Immediate::Chunk &c = E->get(); + + if (c.vertices.empty()) { + continue; + } + + int vertices = c.vertices.size(); + + uint32_t buf_ofs = 0; + + storage->info.render.vertices_count += vertices; + + if (c.texture.is_valid() && storage->texture_owner.owns(c.texture)) { + RasterizerStorageGLES2::Texture *t = storage->texture_owner.get(c.texture); + + t = t->get_ptr(); + + if (t->redraw_if_visible) { + VisualServerRaster::redraw_request(); + } + +#ifdef TOOLS_ENABLED + if (t->detect_3d) { + t->detect_3d(t->detect_3d_ud); + } +#endif + if (t->render_target) { + t->render_target->used_in_frame = true; + } + + glActiveTexture(GL_TEXTURE0); + glBindTexture(t->target, t->tex_id); + restore_tex = true; + } else if (restore_tex) { + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, state.current_main_tex); + restore_tex = false; + } + + if (!c.normals.empty()) { + glEnableVertexAttribArray(VS::ARRAY_NORMAL); + glBufferSubData(GL_ARRAY_BUFFER, buf_ofs, sizeof(Vector3) * vertices, c.normals.ptr()); + glVertexAttribPointer(VS::ARRAY_NORMAL, 3, GL_FLOAT, GL_FALSE, sizeof(Vector3), ((uint8_t *)NULL) + buf_ofs); + buf_ofs += sizeof(Vector3) * vertices; + } else { + glDisableVertexAttribArray(VS::ARRAY_NORMAL); + } + + if (!c.tangents.empty()) { + glEnableVertexAttribArray(VS::ARRAY_TANGENT); + glBufferSubData(GL_ARRAY_BUFFER, buf_ofs, sizeof(Plane) * vertices, c.tangents.ptr()); + glVertexAttribPointer(VS::ARRAY_TANGENT, 4, GL_FLOAT, GL_FALSE, sizeof(Plane), ((uint8_t *)NULL) + buf_ofs); + buf_ofs += sizeof(Plane) * vertices; + } else { + glDisableVertexAttribArray(VS::ARRAY_TANGENT); + } + + if (!c.colors.empty()) { + glEnableVertexAttribArray(VS::ARRAY_COLOR); + glBufferSubData(GL_ARRAY_BUFFER, buf_ofs, sizeof(Color) * vertices, c.colors.ptr()); + glVertexAttribPointer(VS::ARRAY_COLOR, 4, GL_FLOAT, GL_FALSE, sizeof(Color), ((uint8_t *)NULL) + buf_ofs); + buf_ofs += sizeof(Color) * vertices; + } else { + glDisableVertexAttribArray(VS::ARRAY_COLOR); + } + + if (!c.uvs.empty()) { + glEnableVertexAttribArray(VS::ARRAY_TEX_UV); + glBufferSubData(GL_ARRAY_BUFFER, buf_ofs, sizeof(Vector2) * vertices, c.uvs.ptr()); + glVertexAttribPointer(VS::ARRAY_TEX_UV, 2, GL_FLOAT, GL_FALSE, sizeof(Vector2), ((uint8_t *)NULL) + buf_ofs); + buf_ofs += sizeof(Vector2) * vertices; + } else { + glDisableVertexAttribArray(VS::ARRAY_TEX_UV); + } + + if (!c.uv2s.empty()) { + glEnableVertexAttribArray(VS::ARRAY_TEX_UV2); + glBufferSubData(GL_ARRAY_BUFFER, buf_ofs, sizeof(Vector2) * vertices, c.uv2s.ptr()); + glVertexAttribPointer(VS::ARRAY_TEX_UV2, 2, GL_FLOAT, GL_FALSE, sizeof(Vector2), ((uint8_t *)NULL) + buf_ofs); + buf_ofs += sizeof(Vector2) * vertices; + } else { + glDisableVertexAttribArray(VS::ARRAY_TEX_UV2); + } + + glEnableVertexAttribArray(VS::ARRAY_VERTEX); + glBufferSubData(GL_ARRAY_BUFFER, buf_ofs, sizeof(Vector3) * vertices, c.vertices.ptr()); + glVertexAttribPointer(VS::ARRAY_VERTEX, 3, GL_FLOAT, GL_FALSE, sizeof(Vector3), ((uint8_t *)NULL) + buf_ofs); + + glDrawArrays(gl_primitive[c.primitive], 0, c.vertices.size()); + } + + if (restore_tex) { + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, state.current_main_tex); + restore_tex = false; + } + + } break; + } +} + +void RasterizerSceneGLES2::_render_render_list(RenderList::Element **p_elements, int p_element_count, const RID *p_directional_lights, int p_directional_light_count, const Transform &p_view_transform, const CameraMatrix &p_projection, RID p_shadow_atlas, Environment *p_env, GLuint p_base_env, float p_shadow_bias, float p_shadow_normal_bias, bool p_reverse_cull, bool p_alpha_pass, bool p_shadow, bool p_directional_add) { + + ShadowAtlas *shadow_atlas = shadow_atlas_owner.getornull(p_shadow_atlas); + + Vector2 screen_pixel_size; + screen_pixel_size.x = 1.0 / storage->frame.current_rt->width; + screen_pixel_size.y = 1.0 / storage->frame.current_rt->height; + + bool use_radiance_map = false; + + VMap<RID, Vector<RenderList::Element *> > lit_objects; + + for (int i = 0; i < p_element_count; i++) { + RenderList::Element *e = p_elements[i]; + + RasterizerStorageGLES2::Material *material = e->material; + + RasterizerStorageGLES2::Skeleton *skeleton = storage->skeleton_owner.getornull(e->instance->skeleton); + + if (p_base_env) { + glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 2); + glBindTexture(GL_TEXTURE_CUBE_MAP, p_base_env); + use_radiance_map = true; + } + state.scene_shader.set_conditional(SceneShaderGLES2::USE_RADIANCE_MAP, use_radiance_map); + + if (material->shader->spatial.unshaded) { + state.scene_shader.set_conditional(SceneShaderGLES2::USE_RADIANCE_MAP, false); + } else { + state.scene_shader.set_conditional(SceneShaderGLES2::USE_RADIANCE_MAP, use_radiance_map); + } + + // opaque pass + + state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_PASS, false); + + _setup_geometry(e, skeleton); + + _setup_material(material, p_reverse_cull, p_alpha_pass, Size2i(skeleton ? skeleton->size * 3 : 0, 0)); + + if (use_radiance_map) { + state.scene_shader.set_uniform(SceneShaderGLES2::RADIANCE_INVERSE_XFORM, p_view_transform); + } + + if (p_shadow) { + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_BIAS, p_shadow_bias); + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_NORMAL_BIAS, p_shadow_normal_bias); + } + + if (p_env) { + state.scene_shader.set_uniform(SceneShaderGLES2::BG_ENERGY, p_env->bg_energy); + state.scene_shader.set_uniform(SceneShaderGLES2::AMBIENT_SKY_CONTRIBUTION, p_env->ambient_sky_contribution); + state.scene_shader.set_uniform(SceneShaderGLES2::AMBIENT_COLOR, p_env->ambient_color); + state.scene_shader.set_uniform(SceneShaderGLES2::AMBIENT_ENERGY, p_env->ambient_energy); + + } else { + state.scene_shader.set_uniform(SceneShaderGLES2::BG_ENERGY, 1.0); + state.scene_shader.set_uniform(SceneShaderGLES2::AMBIENT_SKY_CONTRIBUTION, 1.0); + state.scene_shader.set_uniform(SceneShaderGLES2::AMBIENT_COLOR, Color(1.0, 1.0, 1.0, 1.0)); + state.scene_shader.set_uniform(SceneShaderGLES2::AMBIENT_ENERGY, 1.0); + } + + glEnable(GL_BLEND); + + if (p_alpha_pass || p_directional_add) { + int desired_blend_mode; + if (p_directional_add) { + desired_blend_mode = RasterizerStorageGLES2::Shader::Spatial::BLEND_MODE_ADD; + } else { + desired_blend_mode = material->shader->spatial.blend_mode; + } + + switch (desired_blend_mode) { + + case RasterizerStorageGLES2::Shader::Spatial::BLEND_MODE_MIX: { + glBlendEquation(GL_FUNC_ADD); + if (storage->frame.current_rt && storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_TRANSPARENT]) { + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + } else { + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + + } break; + case RasterizerStorageGLES2::Shader::Spatial::BLEND_MODE_ADD: { + + glBlendEquation(GL_FUNC_ADD); + glBlendFunc(p_alpha_pass ? GL_SRC_ALPHA : GL_ONE, GL_ONE); + + } break; + case RasterizerStorageGLES2::Shader::Spatial::BLEND_MODE_SUB: { + + glBlendEquation(GL_FUNC_REVERSE_SUBTRACT); + glBlendFunc(GL_SRC_ALPHA, GL_ONE); + } break; + case RasterizerStorageGLES2::Shader::Spatial::BLEND_MODE_MUL: { + glBlendEquation(GL_FUNC_ADD); + if (storage->frame.current_rt && storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_TRANSPARENT]) { + glBlendFuncSeparate(GL_DST_COLOR, GL_ZERO, GL_DST_ALPHA, GL_ZERO); + } else { + glBlendFuncSeparate(GL_DST_COLOR, GL_ZERO, GL_ZERO, GL_ONE); + } + + } break; + } + } else { + // no blend mode given - assume mix + glBlendEquation(GL_FUNC_ADD); + if (storage->frame.current_rt && storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_TRANSPARENT]) { + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + } else { + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + } + + state.scene_shader.set_uniform(SceneShaderGLES2::CAMERA_MATRIX, p_view_transform.inverse()); + state.scene_shader.set_uniform(SceneShaderGLES2::CAMERA_INVERSE_MATRIX, p_view_transform); + state.scene_shader.set_uniform(SceneShaderGLES2::PROJECTION_MATRIX, p_projection); + state.scene_shader.set_uniform(SceneShaderGLES2::PROJECTION_INVERSE_MATRIX, p_projection.inverse()); + + state.scene_shader.set_uniform(SceneShaderGLES2::TIME, storage->frame.time[0]); + + state.scene_shader.set_uniform(SceneShaderGLES2::SCREEN_PIXEL_SIZE, screen_pixel_size); + state.scene_shader.set_uniform(SceneShaderGLES2::NORMAL_MULT, 1.0); // TODO mirror? + state.scene_shader.set_uniform(SceneShaderGLES2::WORLD_TRANSFORM, e->instance->transform); + + _render_geometry(e); + + if (material->shader->spatial.unshaded) + continue; + + if (p_shadow) + continue; + + for (int light = 0; light < e->instance->light_instances.size(); light++) { + + RID light_instance = e->instance->light_instances[light]; + + lit_objects[light_instance].push_back(e); + } + } + + if (p_shadow) { + state.scene_shader.set_conditional(SceneShaderGLES2::USE_RADIANCE_MAP, false); + state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_USE_PSSM4, false); + state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_USE_PSSM2, false); + state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_USE_PSSM_BLEND, false); + return; + } + + state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_PASS, true); + + glEnable(GL_BLEND); + glBlendEquation(GL_FUNC_ADD); + glBlendFunc(GL_SRC_ALPHA, GL_ONE); + + for (int lo = 0; lo < lit_objects.size(); lo++) { + + RID key = lit_objects.getk(lo); + + LightInstance *light = light_instance_owner.getornull(key); + RasterizerStorageGLES2::Light *light_ptr = light->light_ptr; + + const Vector<RenderList::Element *> &list = lit_objects.getv(lo); + + for (int i = 0; i < list.size(); i++) { + + RenderList::Element *e = list[i]; + RasterizerStorageGLES2::Material *material = e->material; + + RasterizerStorageGLES2::Skeleton *skeleton = storage->skeleton_owner.getornull(e->instance->skeleton); + + { + _setup_geometry(e, skeleton); + + _setup_material(material, p_reverse_cull, p_alpha_pass, Size2i(skeleton ? skeleton->size * 3 : 0, 0)); + if (shadow_atlas != NULL) { + glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 4); + glBindTexture(GL_TEXTURE_2D, shadow_atlas->depth); + } + + state.scene_shader.set_uniform(SceneShaderGLES2::CAMERA_MATRIX, p_view_transform.inverse()); + state.scene_shader.set_uniform(SceneShaderGLES2::CAMERA_INVERSE_MATRIX, p_view_transform); + state.scene_shader.set_uniform(SceneShaderGLES2::PROJECTION_MATRIX, p_projection); + state.scene_shader.set_uniform(SceneShaderGLES2::PROJECTION_INVERSE_MATRIX, p_projection.inverse()); + + state.scene_shader.set_uniform(SceneShaderGLES2::TIME, storage->frame.time[0]); + + state.scene_shader.set_uniform(SceneShaderGLES2::SCREEN_PIXEL_SIZE, screen_pixel_size); + state.scene_shader.set_uniform(SceneShaderGLES2::NORMAL_MULT, 1.0); // TODO mirror? + state.scene_shader.set_uniform(SceneShaderGLES2::WORLD_TRANSFORM, e->instance->transform); + } + + switch (light_ptr->type) { + case VS::LIGHT_OMNI: { + + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_TYPE, (int)1); + + Vector3 position = p_view_transform.inverse().xform(light->transform.origin); + + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_POSITION, position); + + float range = light_ptr->param[VS::LIGHT_PARAM_RANGE]; + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_RANGE, range); + + Color attenuation = Color(0.0, 0.0, 0.0, 0.0); + attenuation.a = light_ptr->param[VS::LIGHT_PARAM_ATTENUATION]; + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_ATTENUATION, attenuation); + + if (light_ptr->shadow && shadow_atlas->shadow_owners.has(light->self)) { + + uint32_t key = shadow_atlas->shadow_owners[light->self]; + + uint32_t quadrant = (key >> ShadowAtlas::QUADRANT_SHIFT) & 0x03; + uint32_t shadow = key & ShadowAtlas::SHADOW_INDEX_MASK; + + ERR_CONTINUE(shadow >= (uint32_t)shadow_atlas->quadrants[quadrant].shadows.size()); + + uint32_t atlas_size = shadow_atlas->size; + uint32_t quadrant_size = atlas_size >> 1; + + uint32_t x = (quadrant & 1) * quadrant_size; + uint32_t y = (quadrant >> 1) * quadrant_size; + + uint32_t shadow_size = (quadrant_size / shadow_atlas->quadrants[quadrant].subdivision); + x += (shadow % shadow_atlas->quadrants[quadrant].subdivision) * shadow_size; + y += (shadow / shadow_atlas->quadrants[quadrant].subdivision) * shadow_size; + + uint32_t width = shadow_size; + uint32_t height = shadow_size; + + if (light->light_ptr->omni_shadow_detail == VS::LIGHT_OMNI_SHADOW_DETAIL_HORIZONTAL) { + height /= 2; + } else { + width /= 2; + } + + Transform proj = (p_view_transform.inverse() * light->transform).inverse(); + + Color light_clamp; + light_clamp[0] = float(x) / atlas_size; + light_clamp[1] = float(y) / atlas_size; + light_clamp[2] = float(width) / atlas_size; + light_clamp[3] = float(height) / atlas_size; + + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_SHADOW_MATRIX, proj); + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_CLAMP, light_clamp); + + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_HAS_SHADOW, 1.0); + } else { + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_HAS_SHADOW, 0.0); + } + } break; + + case VS::LIGHT_SPOT: { + Vector3 position = p_view_transform.inverse().xform(light->transform.origin); + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_TYPE, (int)2); + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_POSITION, position); + + Vector3 direction = p_view_transform.inverse().basis.xform(light->transform.basis.xform(Vector3(0, 0, -1))).normalized(); + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_DIRECTION, direction); + Color attenuation = Color(0.0, 0.0, 0.0, 0.0); + attenuation.a = light_ptr->param[VS::LIGHT_PARAM_ATTENUATION]; + float range = light_ptr->param[VS::LIGHT_PARAM_RANGE]; + float spot_attenuation = light_ptr->param[VS::LIGHT_PARAM_SPOT_ATTENUATION]; + float angle = light_ptr->param[VS::LIGHT_PARAM_SPOT_ANGLE]; + angle = Math::cos(Math::deg2rad(angle)); + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_ATTENUATION, attenuation); + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_SPOT_ATTENUATION, spot_attenuation); + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_SPOT_RANGE, spot_attenuation); + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_SPOT_ANGLE, angle); + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_RANGE, range); + + if (light->light_ptr->shadow && shadow_atlas && shadow_atlas->shadow_owners.has(light->self)) { + uint32_t key = shadow_atlas->shadow_owners[light->self]; + + uint32_t quadrant = (key >> ShadowAtlas::QUADRANT_SHIFT) & 0x03; + uint32_t shadow = key & ShadowAtlas::SHADOW_INDEX_MASK; + + ERR_CONTINUE(shadow >= (uint32_t)shadow_atlas->quadrants[quadrant].shadows.size()); + + uint32_t atlas_size = shadow_atlas->size; + uint32_t quadrant_size = atlas_size >> 1; + + uint32_t x = (quadrant & 1) * quadrant_size; + uint32_t y = (quadrant >> 1) * quadrant_size; + + uint32_t shadow_size = (quadrant_size / shadow_atlas->quadrants[quadrant].subdivision); + x += (shadow % shadow_atlas->quadrants[quadrant].subdivision) * shadow_size; + y += (shadow / shadow_atlas->quadrants[quadrant].subdivision) * shadow_size; + + uint32_t width = shadow_size; + uint32_t height = shadow_size; + + Rect2 rect(float(x) / atlas_size, float(y) / atlas_size, float(width) / atlas_size, float(height) / atlas_size); + + Color light_clamp; + light_clamp[0] = rect.position.x; + light_clamp[1] = rect.position.y; + light_clamp[2] = rect.size.x; + light_clamp[3] = rect.size.y; + + Transform modelview = (p_view_transform.inverse() * light->transform).inverse(); + + CameraMatrix bias; + bias.set_light_bias(); + + CameraMatrix rectm; + rectm.set_light_atlas_rect(rect); + + CameraMatrix shadow_matrix = rectm * bias * light->shadow_transform[0].camera * modelview; + + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_HAS_SHADOW, 1.0); + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_SHADOW_MATRIX, shadow_matrix); + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_CLAMP, light_clamp); + + } else { + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_HAS_SHADOW, 0.0); + } + + } break; + + default: break; + } + + float energy = light->light_ptr->param[VS::LIGHT_PARAM_ENERGY]; + float specular = light->light_ptr->param[VS::LIGHT_PARAM_SPECULAR]; + + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_ENERGY, energy); + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_COLOR, light->light_ptr->color.to_linear()); + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_SPECULAR, specular); + + _render_geometry(e); + } + } + + for (int dl = 0; dl < p_directional_light_count; dl++) { + RID light_rid = p_directional_lights[dl]; + LightInstance *light = light_instance_owner.getornull(light_rid); + RasterizerStorageGLES2::Light *light_ptr = light->light_ptr; + + switch (light_ptr->directional_shadow_mode) { + case VS::LIGHT_DIRECTIONAL_SHADOW_ORTHOGONAL: { + } break; + case VS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_2_SPLITS: { + state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_USE_PSSM2, true); + state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_USE_PSSM_BLEND, light_ptr->directional_blend_splits); + } break; + + case VS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_4_SPLITS: { + state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_USE_PSSM4, true); + state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_USE_PSSM_BLEND, light_ptr->directional_blend_splits); + } break; + default: + break; + } + + for (int i = 0; i < p_element_count; i++) { + + RenderList::Element *e = p_elements[i]; + RasterizerStorageGLES2::Material *material = e->material; + RasterizerStorageGLES2::Skeleton *skeleton = storage->skeleton_owner.getornull(e->instance->skeleton); + + { + _setup_material(material, p_reverse_cull, false, Size2i(skeleton ? skeleton->size * 3 : 0, 0)); + + if (directional_shadow.depth) { + glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 4); // TODO move into base pass + glBindTexture(GL_TEXTURE_2D, directional_shadow.depth); + } + + state.scene_shader.set_uniform(SceneShaderGLES2::CAMERA_MATRIX, p_view_transform.inverse()); + state.scene_shader.set_uniform(SceneShaderGLES2::CAMERA_INVERSE_MATRIX, p_view_transform); + state.scene_shader.set_uniform(SceneShaderGLES2::PROJECTION_MATRIX, p_projection); + state.scene_shader.set_uniform(SceneShaderGLES2::PROJECTION_INVERSE_MATRIX, p_projection.inverse()); + + state.scene_shader.set_uniform(SceneShaderGLES2::TIME, storage->frame.time[0]); + + state.scene_shader.set_uniform(SceneShaderGLES2::SCREEN_PIXEL_SIZE, screen_pixel_size); + state.scene_shader.set_uniform(SceneShaderGLES2::NORMAL_MULT, 1.0); // TODO mirror? + state.scene_shader.set_uniform(SceneShaderGLES2::WORLD_TRANSFORM, e->instance->transform); + } + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_TYPE, (int)0); + Vector3 direction = p_view_transform.inverse().basis.xform(light->transform.basis.xform(Vector3(0, 0, -1))).normalized(); + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_DIRECTION, direction); + + float energy = light_ptr->param[VS::LIGHT_PARAM_ENERGY]; + float specular = light_ptr->param[VS::LIGHT_PARAM_SPECULAR]; + + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_ENERGY, energy); + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_SPECULAR, specular); + + float sign = light_ptr->negative ? -1 : 1; + + Color linear_col = light_ptr->color.to_linear(); + Color color; + for (int c = 0; c < 3; c++) + color[c] = linear_col[c] * sign * energy * Math_PI; + + color[3] = 0; + + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_COLOR, color); + + CameraMatrix matrices[4]; + + if (light_ptr->shadow && directional_shadow.depth) { + + int shadow_count = 0; + Color split_offsets; + + switch (light_ptr->directional_shadow_mode) { + case VS::LIGHT_DIRECTIONAL_SHADOW_ORTHOGONAL: { + shadow_count = 1; + } break; + + case VS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_2_SPLITS: { + shadow_count = 2; + } break; + + case VS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_4_SPLITS: { + shadow_count = 4; + } break; + } + + for (int k = 0; k < shadow_count; k++) { + + uint32_t x = light->directional_rect.position.x; + uint32_t y = light->directional_rect.position.y; + uint32_t width = light->directional_rect.size.x; + uint32_t height = light->directional_rect.size.y; + + if (light_ptr->directional_shadow_mode == VS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_4_SPLITS) { + + width /= 2; + height /= 2; + + if (k == 0) { + + } else if (k == 1) { + x += width; + } else if (k == 2) { + y += height; + } else if (k == 3) { + x += width; + y += height; + } + + } else if (light_ptr->directional_shadow_mode == VS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_2_SPLITS) { + + height /= 2; + + if (k == 0) { + + } else { + y += height; + } + } + + split_offsets[k] = light->shadow_transform[k].split; + + Transform modelview = (p_view_transform * light->shadow_transform[k].transform).inverse(); + + CameraMatrix bias; + bias.set_light_bias(); + CameraMatrix rectm; + Rect2 atlas_rect = Rect2(float(x) / directional_shadow.size, float(y) / directional_shadow.size, float(width) / directional_shadow.size, float(height) / directional_shadow.size); + rectm.set_light_atlas_rect(atlas_rect); + + CameraMatrix shadow_mtx = rectm * bias * light->shadow_transform[k].camera * modelview; + matrices[k] = shadow_mtx.inverse(); + + Color light_clamp; + light_clamp[0] = atlas_rect.position.x; + light_clamp[1] = atlas_rect.position.y; + light_clamp[2] = atlas_rect.size.x; + light_clamp[3] = atlas_rect.size.y; + + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_HAS_SHADOW, 1.0); + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_CLAMP, light_clamp); + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_SPLIT_OFFSETS, split_offsets); + } + + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_SHADOW_MATRIX1, matrices[0]); + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_SHADOW_MATRIX2, matrices[1]); + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_SHADOW_MATRIX3, matrices[2]); + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_SHADOW_MATRIX4, matrices[3]); + } else { + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_HAS_SHADOW, 0.0); + } + + _render_geometry(e); + } + } + + state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_PASS, false); + + state.scene_shader.set_conditional(SceneShaderGLES2::USE_RADIANCE_MAP, false); + state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_USE_PSSM4, false); + state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_USE_PSSM2, false); + state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_USE_PSSM_BLEND, false); +} + +void RasterizerSceneGLES2::_draw_sky(RasterizerStorageGLES2::Sky *p_sky, const CameraMatrix &p_projection, const Transform &p_transform, bool p_vflip, float p_custom_fov, float p_energy) { + ERR_FAIL_COND(!p_sky); + + RasterizerStorageGLES2::Texture *tex = storage->texture_owner.getornull(p_sky->panorama); + ERR_FAIL_COND(!tex); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(tex->target, tex->tex_id); + + glDepthMask(GL_TRUE); + glEnable(GL_DEPTH_TEST); + glDisable(GL_CULL_FACE); + glDisable(GL_BLEND); + glDepthFunc(GL_LEQUAL); + glColorMask(1, 1, 1, 1); + + // Camera + CameraMatrix camera; + + if (p_custom_fov) { + + float near_plane = p_projection.get_z_near(); + float far_plane = p_projection.get_z_far(); + float aspect = p_projection.get_aspect(); + + camera.set_perspective(p_custom_fov, aspect, near_plane, far_plane); + } else { + camera = p_projection; + } + + float flip_sign = p_vflip ? -1 : 1; + + // If matrix[2][0] or matrix[2][1] we're dealing with an asymmetrical projection matrix. This is the case for stereoscopic rendering (i.e. VR). + // To ensure the image rendered is perspective correct we need to move some logic into the shader. For this the USE_ASYM_PANO option is introduced. + // It also means the uv coordinates are ignored in this mode and we don't need our loop. + bool asymmetrical = ((camera.matrix[2][0] != 0.0) || (camera.matrix[2][1] != 0.0)); + + Vector3 vertices[8] = { + Vector3(-1, -1 * flip_sign, 1), + Vector3(0, 1, 0), + Vector3(1, -1 * flip_sign, 1), + Vector3(1, 1, 0), + Vector3(1, 1 * flip_sign, 1), + Vector3(1, 0, 0), + Vector3(-1, 1 * flip_sign, 1), + Vector3(0, 0, 0), + }; + + if (!asymmetrical) { + float vw, vh, zn; + camera.get_viewport_size(vw, vh); + zn = p_projection.get_z_near(); + + for (int i = 0; i < 4; i++) { + Vector3 uv = vertices[i * 2 + 1]; + uv.x = (uv.x * 2.0 - 1.0) * vw; + uv.y = -(uv.y * 2.0 - 1.0) * vh; + uv.z = -zn; + vertices[i * 2 + 1] = p_transform.basis.xform(uv).normalized(); + vertices[i * 2 + 1].z = -vertices[i * 2 + 1].z; + } + } + + glBindBuffer(GL_ARRAY_BUFFER, state.sky_verts); + glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(Vector3) * 8, vertices); + + // bind sky vertex array.... + glVertexAttribPointer(VS::ARRAY_VERTEX, 3, GL_FLOAT, GL_FALSE, sizeof(Vector3) * 2, 0); + glVertexAttribPointer(VS::ARRAY_TEX_UV, 3, GL_FLOAT, GL_FALSE, sizeof(Vector3) * 2, ((uint8_t *)NULL) + sizeof(Vector3)); + glEnableVertexAttribArray(VS::ARRAY_VERTEX); + glEnableVertexAttribArray(VS::ARRAY_TEX_UV); + + storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_MULTIPLIER, true); + storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_CUBEMAP, false); + storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_PANORAMA, true); + storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_COPY_SECTION, false); + storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_CUSTOM_ALPHA, false); + storage->shaders.copy.bind(); + storage->shaders.copy.set_uniform(CopyShaderGLES2::MULTIPLIER, p_energy); + + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + + glDisableVertexAttribArray(VS::ARRAY_VERTEX); + glDisableVertexAttribArray(VS::ARRAY_TEX_UV); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_MULTIPLIER, false); + storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_CUBEMAP, false); +} + void RasterizerSceneGLES2::render_scene(const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID *p_light_cull_result, int p_light_cull_count, RID *p_reflection_probe_cull_result, int p_reflection_probe_cull_count, RID p_environment, RID p_shadow_atlas, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass) { + + glEnable(GL_BLEND); + + GLuint current_fb = storage->frame.current_rt->fbo; + Environment *env = environment_owner.getornull(p_environment); + + // render list stuff + + render_list.clear(); + _fill_render_list(p_cull_result, p_cull_count, false, false); + + // other stuff + + glBindFramebuffer(GL_FRAMEBUFFER, current_fb); + + glDepthFunc(GL_LEQUAL); + glDepthMask(GL_TRUE); + glClearDepth(1.0f); + glEnable(GL_DEPTH_TEST); + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + storage->frame.clear_request = false; + + glVertexAttrib4f(VS::ARRAY_COLOR, 1, 1, 1, 1); + + glBlendEquation(GL_FUNC_ADD); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + // render sky + RasterizerStorageGLES2::Sky *sky = NULL; + GLuint env_radiance_tex = 0; + if (env) { + switch (env->bg_mode) { + + case VS::ENV_BG_COLOR_SKY: + case VS::ENV_BG_SKY: { + sky = storage->sky_owner.getornull(env->sky); + + if (sky) { + env_radiance_tex = sky->radiance; + } + } break; + + default: { + // FIXME: implement other background modes + } break; + } + } + + if (env && env->bg_mode == VS::ENV_BG_SKY && (!storage->frame.current_rt || !storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_TRANSPARENT])) { + + if (sky && sky->panorama.is_valid()) { + _draw_sky(sky, p_cam_projection, p_cam_transform, false, env->sky_custom_fov, env->bg_energy); + } + } + + Vector<RID> directional_lights; + + for (int i = 0; i < p_light_cull_count; i++) { + RID light_rid = p_light_cull_result[i]; + + LightInstance *light = light_instance_owner.getornull(light_rid); + + if (light->light_ptr->type == VS::LIGHT_DIRECTIONAL) { + directional_lights.push_back(light_rid); + } + } + + // render opaque things first + render_list.sort_by_key(false); + _render_render_list(render_list.elements, render_list.element_count, directional_lights.ptr(), directional_lights.size(), p_cam_transform, p_cam_projection, p_shadow_atlas, env, env_radiance_tex, 0.0, 0.0, false, false, false, false); + + // alpha pass + + glBlendEquation(GL_FUNC_ADD); + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + + render_list.sort_by_key(true); + _render_render_list(&render_list.elements[render_list.max_elements - render_list.alpha_element_count], render_list.alpha_element_count, directional_lights.ptr(), directional_lights.size(), p_cam_transform, p_cam_projection, p_shadow_atlas, env, env_radiance_tex, 0.0, 0.0, false, true, false, false); + + glDepthMask(GL_FALSE); + glDisable(GL_DEPTH_TEST); + + // #define GLES2_SHADOW_ATLAS_DEBUG_VIEW + +#ifdef GLES2_SHADOW_ATLAS_DEBUG_VIEW + ShadowAtlas *shadow_atlas = shadow_atlas_owner.getornull(p_shadow_atlas); + if (shadow_atlas) { + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, shadow_atlas->depth); + + glViewport(0, 0, storage->frame.current_rt->width / 4, storage->frame.current_rt->height / 4); + storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_CUBEMAP, false); + storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_COPY_SECTION, false); + storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_CUSTOM_ALPHA, false); + storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_MULTIPLIER, false); + storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_PANORAMA, false); + storage->shaders.copy.bind(); + + storage->_copy_screen(); + } +#endif } void RasterizerSceneGLES2::render_shadow(RID p_light, RID p_shadow_atlas, int p_pass, InstanceBase **p_cull_result, int p_cull_count) { + + LightInstance *light_instance = light_instance_owner.getornull(p_light); + ERR_FAIL_COND(!light_instance); + + RasterizerStorageGLES2::Light *light = light_instance->light_ptr; + ERR_FAIL_COND(!light); + + uint32_t x; + uint32_t y; + uint32_t width; + uint32_t height; + uint32_t vp_height; + + float zfar = 0; + bool flip_facing = false; + int custom_vp_size = 0; + + GLuint fbo = 0; + + int current_cubemap = -1; + float bias = 0; + float normal_bias = 0; + + CameraMatrix light_projection; + Transform light_transform; + + // TODO directional light + + if (light->type == VS::LIGHT_DIRECTIONAL) { + // set pssm stuff + + // TODO set this only when changed + + light_instance->light_directional_index = directional_shadow.current_light; + light_instance->last_scene_shadow_pass = scene_pass; + + directional_shadow.current_light++; + + if (directional_shadow.light_count == 1) { + light_instance->directional_rect = Rect2(0, 0, directional_shadow.size, directional_shadow.size); + } else if (directional_shadow.light_count == 2) { + light_instance->directional_rect = Rect2(0, 0, directional_shadow.size, directional_shadow.size / 2); + if (light_instance->light_directional_index == 1) { + light_instance->directional_rect.position.x += light_instance->directional_rect.size.x; + } + } else { //3 and 4 + light_instance->directional_rect = Rect2(0, 0, directional_shadow.size / 2, directional_shadow.size / 2); + if (light_instance->light_directional_index & 1) { + light_instance->directional_rect.position.x += light_instance->directional_rect.size.x; + } + if (light_instance->light_directional_index / 2) { + light_instance->directional_rect.position.y += light_instance->directional_rect.size.y; + } + } + + light_projection = light_instance->shadow_transform[p_pass].camera; + light_transform = light_instance->shadow_transform[p_pass].transform; + + x = light_instance->directional_rect.position.x; + y = light_instance->directional_rect.position.y; + width = light_instance->directional_rect.size.width; + height = light_instance->directional_rect.size.height; + + if (light->directional_shadow_mode == VS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_4_SPLITS) { + + width /= 2; + height /= 2; + + if (p_pass == 0) { + + } else if (p_pass == 1) { + x += width; + } else if (p_pass == 2) { + y += height; + } else if (p_pass == 3) { + x += width; + y += height; + } + + } else if (light->directional_shadow_mode == VS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_2_SPLITS) { + + height /= 2; + + if (p_pass == 0) { + + } else { + y += height; + } + } + + float bias_mult = Math::lerp(1.0f, light_instance->shadow_transform[p_pass].bias_scale, light->param[VS::LIGHT_PARAM_SHADOW_BIAS_SPLIT_SCALE]); + zfar = light->param[VS::LIGHT_PARAM_RANGE]; + bias = light->param[VS::LIGHT_PARAM_SHADOW_BIAS] * bias_mult; + normal_bias = light->param[VS::LIGHT_PARAM_SHADOW_NORMAL_BIAS] * bias_mult; + + fbo = directional_shadow.fbo; + vp_height = directional_shadow.size; + } else { + ShadowAtlas *shadow_atlas = shadow_atlas_owner.getornull(p_shadow_atlas); + ERR_FAIL_COND(!shadow_atlas); + ERR_FAIL_COND(!shadow_atlas->shadow_owners.has(p_light)); + + fbo = shadow_atlas->fbo; + vp_height = shadow_atlas->size; + + uint32_t key = shadow_atlas->shadow_owners[p_light]; + + uint32_t quadrant = (key >> ShadowAtlas::QUADRANT_SHIFT) & 0x03; + uint32_t shadow = key & ShadowAtlas::SHADOW_INDEX_MASK; + + ERR_FAIL_INDEX((int)shadow, shadow_atlas->quadrants[quadrant].shadows.size()); + + uint32_t quadrant_size = shadow_atlas->size >> 1; + + x = (quadrant & 1) * quadrant_size; + y = (quadrant >> 1) * quadrant_size; + + uint32_t shadow_size = (quadrant_size / shadow_atlas->quadrants[quadrant].subdivision); + x += (shadow % shadow_atlas->quadrants[quadrant].subdivision) * shadow_size; + y += (shadow / shadow_atlas->quadrants[quadrant].subdivision) * shadow_size; + + width = shadow_size; + height = shadow_size; + + if (light->type == VS::LIGHT_OMNI) { + // cubemap only + if (light->omni_shadow_mode == VS::LIGHT_OMNI_SHADOW_CUBE) { + int cubemap_index = shadow_cubemaps.size() - 1; + + // find an appropriate cubemap to render to + for (int i = shadow_cubemaps.size() - 1; i >= 0; i--) { + if (shadow_cubemaps[i].size > shadow_size * 2) { + break; + } + + cubemap_index = i; + } + + fbo = shadow_cubemaps[cubemap_index].fbo[p_pass]; + light_projection = light_instance->shadow_transform[0].camera; + light_transform = light_instance->shadow_transform[0].transform; + + custom_vp_size = shadow_cubemaps[cubemap_index].size; + zfar = light->param[VS::LIGHT_PARAM_RANGE]; + + current_cubemap = cubemap_index; + } + } else { + light_projection = light_instance->shadow_transform[0].camera; + light_transform = light_instance->shadow_transform[0].transform; + + flip_facing = false; + zfar = light->param[VS::LIGHT_PARAM_RANGE]; + bias = light->param[VS::LIGHT_PARAM_SHADOW_BIAS]; + normal_bias = light->param[VS::LIGHT_PARAM_SHADOW_NORMAL_BIAS]; + } + } + + render_list.clear(); + + _fill_render_list(p_cull_result, p_cull_count, true, true); + + render_list.sort_by_depth(false); + + glDisable(GL_BLEND); + glDisable(GL_DITHER); + glEnable(GL_DEPTH_TEST); + + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + + glDepthMask(GL_TRUE); + glColorMask(0, 0, 0, 0); + + if (custom_vp_size) { + glViewport(0, 0, custom_vp_size, custom_vp_size); + glScissor(0, 0, custom_vp_size, custom_vp_size); + } else { + glViewport(x, y, width, height); + glScissor(x, y, width, height); + } + + glEnable(GL_SCISSOR_TEST); + glClearDepth(1.0f); + glClear(GL_DEPTH_BUFFER_BIT); + glDisable(GL_SCISSOR_TEST); + + state.scene_shader.set_conditional(SceneShaderGLES2::RENDER_DEPTH, true); + + _render_render_list(render_list.elements, render_list.element_count, NULL, 0, light_transform, light_projection, RID(), NULL, 0, bias, normal_bias, false, false, true, false); + + state.scene_shader.set_conditional(SceneShaderGLES2::RENDER_DEPTH, false); + + // convert cubemap to dual paraboloid if needed + if (light->type == VS::LIGHT_OMNI && light->omni_shadow_mode == VS::LIGHT_OMNI_SHADOW_CUBE && p_pass == 5) { + ShadowAtlas *shadow_atlas = shadow_atlas_owner.getornull(p_shadow_atlas); + + glBindFramebuffer(GL_FRAMEBUFFER, shadow_atlas->fbo); + state.cube_to_dp_shader.bind(); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_CUBE_MAP, shadow_cubemaps[current_cubemap].cubemap); + + glDisable(GL_CULL_FACE); + + for (int i = 0; i < 2; i++) { + state.cube_to_dp_shader.set_uniform(CubeToDpShaderGLES2::Z_FLIP, i == 1); + state.cube_to_dp_shader.set_uniform(CubeToDpShaderGLES2::Z_NEAR, light_projection.get_z_near()); + state.cube_to_dp_shader.set_uniform(CubeToDpShaderGLES2::Z_FAR, light_projection.get_z_far()); + state.cube_to_dp_shader.set_uniform(CubeToDpShaderGLES2::BIAS, light->param[VS::LIGHT_PARAM_SHADOW_BIAS]); + + uint32_t local_width = width; + uint32_t local_height = height; + uint32_t local_x = x; + uint32_t local_y = y; + + if (light->omni_shadow_detail == VS::LIGHT_OMNI_SHADOW_DETAIL_HORIZONTAL) { + local_height /= 2; + local_y += i * local_height; + } else { + local_width /= 2; + local_x += i * local_width; + } + + glViewport(local_x, local_y, local_width, local_height); + glScissor(local_x, local_y, local_width, local_height); + + glEnable(GL_SCISSOR_TEST); + + glClearDepth(1.0f); + + glClear(GL_DEPTH_BUFFER_BIT); + glDisable(GL_SCISSOR_TEST); + + glDisable(GL_BLEND); + + storage->_copy_screen(); + } + } + + glViewport(0, 0, storage->frame.current_rt->width, storage->frame.current_rt->height); } void RasterizerSceneGLES2::set_scene_pass(uint64_t p_pass) { + scene_pass = p_pass; } bool RasterizerSceneGLES2::free(RID p_rid) { @@ -223,6 +2360,109 @@ void RasterizerSceneGLES2::set_debug_draw_mode(VS::ViewportDebugDraw p_debug_dra } void RasterizerSceneGLES2::initialize() { + state.scene_shader.init(); + state.cube_to_dp_shader.init(); + + render_list.init(); + + shadow_atlas_realloc_tolerance_msec = 500; + + { + //default material and shader + + default_shader = storage->shader_create(); + storage->shader_set_code(default_shader, "shader_type spatial;\n"); + default_material = storage->material_create(); + storage->material_set_shader(default_material, default_shader); + + default_shader_twosided = storage->shader_create(); + default_material_twosided = storage->material_create(); + storage->shader_set_code(default_shader_twosided, "shader_type spatial; render_mode cull_disabled;\n"); + storage->material_set_shader(default_material_twosided, default_shader_twosided); + } + + { + glGenBuffers(1, &state.sky_verts); + glBindBuffer(GL_ARRAY_BUFFER, state.sky_verts); + glBufferData(GL_ARRAY_BUFFER, sizeof(Vector3) * 8, NULL, GL_DYNAMIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + } + + { + uint32_t immediate_buffer_size = GLOBAL_DEF("rendering/limits/buffers/immediate_buffer_size_kb", 2048); + + glGenBuffers(1, &state.immediate_buffer); + glBindBuffer(GL_ARRAY_BUFFER, state.immediate_buffer); + glBufferData(GL_ARRAY_BUFFER, immediate_buffer_size * 1024, NULL, GL_DYNAMIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + } + + // cubemaps for shadows + { + int max_shadow_cubemap_sampler_size = 512; + + int cube_size = max_shadow_cubemap_sampler_size; + + glActiveTexture(GL_TEXTURE0); + + while (cube_size >= 32) { + + ShadowCubeMap cube; + + cube.size = cube_size; + + glGenTextures(1, &cube.cubemap); + glBindTexture(GL_TEXTURE_CUBE_MAP, cube.cubemap); + + for (int i = 0; i < 6; i++) { + glTexImage2D(_cube_side_enum[i], 0, GL_DEPTH_COMPONENT16, cube_size, cube_size, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, NULL); + } + + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glGenFramebuffers(6, cube.fbo); + for (int i = 0; i < 6; i++) { + + glBindFramebuffer(GL_FRAMEBUFFER, cube.fbo[i]); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, _cube_side_enum[i], cube.cubemap, 0); + } + + shadow_cubemaps.push_back(cube); + + cube_size >>= 1; + } + } + + { + // directional shadows + + directional_shadow.light_count = 0; + directional_shadow.size = next_power_of_2(GLOBAL_GET("rendering/quality/directional_shadow/size")); + + glGenFramebuffers(1, &directional_shadow.fbo); + glBindFramebuffer(GL_FRAMEBUFFER, directional_shadow.fbo); + + glGenTextures(1, &directional_shadow.depth); + glBindTexture(GL_TEXTURE_2D, directional_shadow.depth); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT16, directional_shadow.size, directional_shadow.size, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, directional_shadow.depth, 0); + + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) { + ERR_PRINT("Directional shadow framebuffer status invalid"); + } + } } void RasterizerSceneGLES2::iteration() { diff --git a/drivers/gles2/rasterizer_scene_gles2.h b/drivers/gles2/rasterizer_scene_gles2.h index 110222f709..72dbe14387 100644 --- a/drivers/gles2/rasterizer_scene_gles2.h +++ b/drivers/gles2/rasterizer_scene_gles2.h @@ -33,10 +33,10 @@ /* Must come before shaders or the Windows build fails... */ #include "rasterizer_storage_gles2.h" +#include "shaders/cube_to_dp.glsl.gen.h" #include "shaders/scene.glsl.gen.h" /* -#include "drivers/gles3/shaders/cube_to_dp.glsl.gen.h" #include "drivers/gles3/shaders/effect_blur.glsl.gen.h" #include "drivers/gles3/shaders/exposure.glsl.gen.h" #include "drivers/gles3/shaders/resolve.glsl.gen.h" @@ -52,6 +52,13 @@ class RasterizerSceneGLES2 : public RasterizerScene { public: + RID default_material; + RID default_material_twosided; + RID default_shader; + RID default_shader_twosided; + + uint64_t scene_pass; + RasterizerStorageGLES2 *storage; struct State { @@ -63,7 +70,12 @@ public: GLuint current_main_tex; SceneShaderGLES2 scene_shader; - // CubeToDpShaderGLES3 cube_to_dp_shader; + CubeToDpShaderGLES2 cube_to_dp_shader; + + GLuint sky_verts; + + GLuint immediate_buffer; + // ResolveShaderGLES3 resolve_shader; // ScreenSpaceReflectionShaderGLES3 ssr_shader; // EffectBlurShaderGLES3 effect_blur_shader; @@ -128,7 +140,6 @@ public: GLuint env_radiance_ubo; - GLuint sky_verts; GLuint sky_array; GLuint directional_ubo; @@ -169,11 +180,72 @@ public: /* SHADOW ATLAS API */ + uint64_t shadow_atlas_realloc_tolerance_msec; + + struct ShadowAtlas : public RID_Data { + enum { + QUADRANT_SHIFT = 27, + SHADOW_INDEX_MASK = (1 << QUADRANT_SHIFT) - 1, + SHADOW_INVALID = 0xFFFFFFFF, + }; + + struct Quadrant { + uint32_t subdivision; + + struct Shadow { + RID owner; + uint64_t version; + uint64_t alloc_tick; + + Shadow() { + version = 0; + alloc_tick = 0; + } + }; + + Vector<Shadow> shadows; + + Quadrant() { + subdivision = 0; + } + } quadrants[4]; + + int size_order[4]; + uint32_t smallest_subdiv; + + int size; + + GLuint fbo; + GLuint depth; + + Map<RID, uint32_t> shadow_owners; + }; + + struct ShadowCubeMap { + GLuint fbo[6]; + GLuint cubemap; + uint32_t size; + }; + + Vector<ShadowCubeMap> shadow_cubemaps; + + RID_Owner<ShadowAtlas> shadow_atlas_owner; + RID shadow_atlas_create(); void shadow_atlas_set_size(RID p_atlas, int p_size); void shadow_atlas_set_quadrant_subdivision(RID p_atlas, int p_quadrant, int p_subdivision); + bool _shadow_atlas_find_shadow(ShadowAtlas *shadow_atlas, int *p_in_quadrants, int p_quadrant_count, int p_current_subdiv, uint64_t p_tick, int &r_quadrant, int &r_shadow); bool shadow_atlas_update_light(RID p_atlas, RID p_light_intance, float p_coverage, uint64_t p_light_version); + struct DirectionalShadow { + GLuint fbo; + GLuint depth; + + int light_count; + int size; + int current_light; + } directional_shadow; + virtual int get_directional_light_shadow_size(RID p_light_intance); virtual void set_directional_shadow_count(int p_count); @@ -196,6 +268,36 @@ public: virtual bool reflection_probe_instance_postprocess_step(RID p_instance); /* ENVIRONMENT API */ + + struct Environment : public RID_Data { + VS::EnvironmentBG bg_mode; + + RID sky; + float sky_custom_fov; + + Color bg_color; + float bg_energy; + float sky_ambient; + + Color ambient_color; + float ambient_energy; + float ambient_sky_contribution; + + int canvas_max_layer; + + Environment() { + bg_mode = VS::ENV_BG_CLEAR_COLOR; + sky_custom_fov = 0.0; + bg_energy = 1.0; + sky_ambient = 0; + ambient_energy = 1.0; + ambient_sky_contribution = 0.0; + canvas_max_layer = 0; + } + }; + + mutable RID_Owner<Environment> environment_owner; + virtual RID environment_create(); virtual void environment_set_background(RID p_env, VS::EnvironmentBG p_bg); @@ -228,6 +330,43 @@ public: virtual int environment_get_canvas_max_layer(RID p_env); /* LIGHT INSTANCE */ + + struct LightInstance : public RID_Data { + + struct ShadowTransform { + CameraMatrix camera; + Transform transform; + float farplane; + float split; + float bias_scale; + }; + + ShadowTransform shadow_transform[4]; + + RID self; + RID light; + + RasterizerStorageGLES2::Light *light_ptr; + Transform transform; + + Vector3 light_vector; + Vector3 spot_vector; + float linear_att; + + // TODO passes and all that stuff ? + uint64_t last_scene_pass; + uint64_t last_scene_shadow_pass; + + uint16_t light_index; + uint16_t light_directional_index; + + Rect2 directional_rect; + + Set<RID> shadow_atlases; // atlases where this light is registered + }; + + mutable RID_Owner<LightInstance> light_instance_owner; + virtual RID light_instance_create(RID p_light); virtual void light_instance_set_transform(RID p_light_instance, const Transform &p_transform); virtual void light_instance_set_shadow_transform(RID p_light_instance, const CameraMatrix &p_projection, const Transform &p_transform, float p_far, float p_split, int p_pass, float p_bias_scale = 1.0); @@ -242,6 +381,192 @@ public: /* RENDER LIST */ + struct RenderList { + enum { + DEFAULT_MAX_ELEMENTS = 65536, + SORT_FLAG_SKELETON = 1, + SORT_FLAG_INSTANCING = 2, + MAX_DIRECTIONAL_LIGHTS = 16, + MAX_LIGHTS = 4096, + MAX_REFLECTIONS = 1024, + + SORT_KEY_PRIORITY_SHIFT = 56, + SORT_KEY_PRIORITY_MASK = 0xFF, + //depth layer for opaque (56-52) + SORT_KEY_OPAQUE_DEPTH_LAYER_SHIFT = 52, + SORT_KEY_OPAQUE_DEPTH_LAYER_MASK = 0xF, +//64 bits unsupported in MSVC +#define SORT_KEY_UNSHADED_FLAG (uint64_t(1) << 49) +#define SORT_KEY_NO_DIRECTIONAL_FLAG (uint64_t(1) << 48) +#define SORT_KEY_LIGHTMAP_CAPTURE_FLAG (uint64_t(1) << 47) +#define SORT_KEY_LIGHTMAP_FLAG (uint64_t(1) << 46) +#define SORT_KEY_GI_PROBES_FLAG (uint64_t(1) << 45) +#define SORT_KEY_VERTEX_LIT_FLAG (uint64_t(1) << 44) + SORT_KEY_SHADING_SHIFT = 44, + SORT_KEY_SHADING_MASK = 63, + //44-28 material index + SORT_KEY_MATERIAL_INDEX_SHIFT = 28, + //28-8 geometry index + SORT_KEY_GEOMETRY_INDEX_SHIFT = 8, + //bits 5-7 geometry type + SORT_KEY_GEOMETRY_TYPE_SHIFT = 5, + //bits 0-5 for flags + SORT_KEY_OPAQUE_PRE_PASS = 8, + SORT_KEY_CULL_DISABLED_FLAG = 4, + SORT_KEY_SKELETON_FLAG = 2, + SORT_KEY_MIRROR_FLAG = 1 + }; + + int max_elements; + + struct Element { + RasterizerScene::InstanceBase *instance; + + RasterizerStorageGLES2::Geometry *geometry; + RasterizerStorageGLES2::Material *material; + RasterizerStorageGLES2::GeometryOwner *owner; + + uint64_t sort_key; + }; + + Element *base_elements; + Element **elements; + + int element_count; + int alpha_element_count; + + void clear() { + element_count = 0; + alpha_element_count = 0; + } + + // sorts + + struct SortByKey { + _FORCE_INLINE_ bool operator()(const Element *A, const Element *B) const { + return A->sort_key < B->sort_key; + } + }; + + void sort_by_key(bool p_alpha) { + SortArray<Element *, SortByKey> sorter; + + if (p_alpha) { + sorter.sort(&elements[max_elements - alpha_element_count], alpha_element_count); + } else { + sorter.sort(elements, element_count); + } + } + + struct SortByDepth { + + _FORCE_INLINE_ bool operator()(const Element *A, const Element *B) const { + return A->instance->depth < B->instance->depth; + } + }; + + void sort_by_depth(bool p_alpha) { //used for shadows + + SortArray<Element *, SortByDepth> sorter; + if (p_alpha) { + sorter.sort(&elements[max_elements - alpha_element_count], alpha_element_count); + } else { + sorter.sort(elements, element_count); + } + } + + struct SortByReverseDepthAndPriority { + + _FORCE_INLINE_ bool operator()(const Element *A, const Element *B) const { + uint32_t layer_A = uint32_t(A->sort_key >> SORT_KEY_PRIORITY_SHIFT); + uint32_t layer_B = uint32_t(B->sort_key >> SORT_KEY_PRIORITY_SHIFT); + if (layer_A == layer_B) { + return A->instance->depth > B->instance->depth; + } else { + return layer_A < layer_B; + } + } + }; + + void sort_by_reverse_depth_and_priority(bool p_alpha) { //used for alpha + + SortArray<Element *, SortByReverseDepthAndPriority> sorter; + if (p_alpha) { + sorter.sort(&elements[max_elements - alpha_element_count], alpha_element_count); + } else { + sorter.sort(elements, element_count); + } + } + + // element adding and stuff + + _FORCE_INLINE_ Element *add_element() { + if (element_count + alpha_element_count >= max_elements) + return NULL; + + elements[element_count] = &base_elements[element_count]; + return elements[element_count++]; + } + + _FORCE_INLINE_ Element *add_alpha_element() { + if (element_count + alpha_element_count >= max_elements) { + return NULL; + } + + int idx = max_elements - alpha_element_count - 1; + elements[idx] = &base_elements[idx]; + alpha_element_count++; + return elements[idx]; + } + + void init() { + element_count = 0; + alpha_element_count = 0; + + elements = memnew_arr(Element *, max_elements); + base_elements = memnew_arr(Element, max_elements); + + for (int i = 0; i < max_elements; i++) { + elements[i] = &base_elements[i]; + } + } + + RenderList() { + max_elements = DEFAULT_MAX_ELEMENTS; + } + + ~RenderList() { + memdelete_arr(elements); + memdelete_arr(base_elements); + } + }; + + RenderList render_list; + + void _add_geometry(RasterizerStorageGLES2::Geometry *p_geometry, InstanceBase *p_instance, RasterizerStorageGLES2::GeometryOwner *p_owner, int p_material, bool p_depth_pass, bool p_shadow_pass); + void _add_geometry_with_material(RasterizerStorageGLES2::Geometry *p_geometry, InstanceBase *p_instance, RasterizerStorageGLES2::GeometryOwner *p_owner, RasterizerStorageGLES2::Material *p_material, bool p_depth_pass, bool p_shadow_pass); + + void _fill_render_list(InstanceBase **p_cull_result, int p_cull_count, bool p_depth_pass, bool p_shadow_pass); + void _render_render_list(RenderList::Element **p_elements, int p_element_count, + const RID *p_directional_lights, int p_directional_light_count, + const Transform &p_view_transform, + const CameraMatrix &p_projection, + RID p_shadow_atlas, + Environment *p_env, + GLuint p_base_env, + float p_shadow_bias, + float p_shadow_normal_bias, + bool p_reverse_cull, + bool p_alpha_pass, + bool p_shadow, + bool p_directional_add); + + void _draw_sky(RasterizerStorageGLES2::Sky *p_sky, const CameraMatrix &p_projection, const Transform &p_transform, bool p_vflip, float p_custom_fov, float p_energy); + + void _setup_material(RasterizerStorageGLES2::Material *p_material, bool p_reverse_cull, bool p_alpha_pass, Size2i p_skeleton_tex_size = Size2i(0, 0)); + void _setup_geometry(RenderList::Element *p_element, RasterizerStorageGLES2::Skeleton *p_skeleton); + void _render_geometry(RenderList::Element *p_element); + virtual void render_scene(const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID *p_light_cull_result, int p_light_cull_count, RID *p_reflection_probe_cull_result, int p_reflection_probe_cull_count, RID p_environment, RID p_shadow_atlas, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass); virtual void render_shadow(RID p_light, RID p_shadow_atlas, int p_pass, InstanceBase **p_cull_result, int p_cull_count); virtual bool free(RID p_rid); diff --git a/drivers/gles2/rasterizer_storage_gles2.cpp b/drivers/gles2/rasterizer_storage_gles2.cpp index ca39531b0d..262add1cc3 100644 --- a/drivers/gles2/rasterizer_storage_gles2.cpp +++ b/drivers/gles2/rasterizer_storage_gles2.cpp @@ -32,14 +32,41 @@ #include "rasterizer_canvas_gles2.h" #include "rasterizer_scene_gles2.h" +#include "math/transform.h" + +#include "servers/visual/shader_language.h" + GLuint RasterizerStorageGLES2::system_fbo = 0; /* TEXTURE API */ -Ref<Image> RasterizerStorageGLES2::_get_gl_image_and_format(const Ref<Image> &p_image, Image::Format p_format, uint32_t p_flags, GLenum &r_gl_format, GLenum &r_gl_internal_format, GLenum &r_gl_type) { +#define _EXT_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1 +#define _EXT_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2 +#define _EXT_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3 + +#define _EXT_ETC1_RGB8_OES 0x8D64 + +#ifdef GLES_OVER_GL +#define _GL_HALF_FLOAT_OES 0x140B +#else +#define _GL_HALF_FLOAT_OES 0x8D61 +#endif + +void RasterizerStorageGLES2::bind_quad_array() const { + glBindBuffer(GL_ARRAY_BUFFER, resources.quadie); + glVertexAttribPointer(VS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, 0); + glVertexAttribPointer(VS::ARRAY_TEX_UV, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, ((uint8_t *)NULL) + 8); + + glEnableVertexAttribArray(VS::ARRAY_VERTEX); + glEnableVertexAttribArray(VS::ARRAY_TEX_UV); +} + +Ref<Image> RasterizerStorageGLES2::_get_gl_image_and_format(const Ref<Image> &p_image, Image::Format p_format, uint32_t p_flags, Image::Format &r_real_format, GLenum &r_gl_format, GLenum &r_gl_internal_format, GLenum &r_gl_type, bool &r_compressed) const { r_gl_format = 0; Ref<Image> image = p_image; + r_compressed = false; + r_real_format = p_format; bool need_decompress = false; @@ -98,9 +125,14 @@ Ref<Image> RasterizerStorageGLES2::_get_gl_image_and_format(const Ref<Image> &p_ } break; case Image::FORMAT_RF: { - ERR_EXPLAIN("R float texture not supported"); - ERR_FAIL_V(image); + if (!config.float_texture_supported) { + ERR_EXPLAIN("R float texture not supported"); + ERR_FAIL_V(image); + } + r_gl_internal_format = GL_ALPHA; + r_gl_format = GL_ALPHA; + r_gl_type = GL_FLOAT; } break; case Image::FORMAT_RGF: { ERR_EXPLAIN("RG float texture not supported"); @@ -108,54 +140,87 @@ Ref<Image> RasterizerStorageGLES2::_get_gl_image_and_format(const Ref<Image> &p_ } break; case Image::FORMAT_RGBF: { + if (!config.float_texture_supported) { - ERR_EXPLAIN("RGB float texture not supported"); - ERR_FAIL_V(image); + ERR_EXPLAIN("RGB float texture not supported"); + ERR_FAIL_V(image); + } + + r_gl_internal_format = GL_RGB; + r_gl_format = GL_RGB; + r_gl_type = GL_FLOAT; } break; case Image::FORMAT_RGBAF: { + if (!config.float_texture_supported) { - ERR_EXPLAIN("RGBA float texture not supported"); - ERR_FAIL_V(image); + ERR_EXPLAIN("RGBA float texture not supported"); + ERR_FAIL_V(image); + } + + r_gl_internal_format = GL_RGBA; + r_gl_format = GL_RGBA; + r_gl_type = GL_FLOAT; } break; case Image::FORMAT_RH: { - ERR_EXPLAIN("R half float texture not supported"); - ERR_FAIL_V(image); + need_decompress = true; } break; case Image::FORMAT_RGH: { - ERR_EXPLAIN("RG half float texture not supported"); - ERR_FAIL_V(image); - + need_decompress = true; } break; case Image::FORMAT_RGBH: { - ERR_EXPLAIN("RGB half float texture not supported"); - ERR_FAIL_V(image); - + need_decompress = true; } break; case Image::FORMAT_RGBAH: { - ERR_EXPLAIN("RGBA half float texture not supported"); - ERR_FAIL_V(image); - + need_decompress = true; } break; case Image::FORMAT_RGBE9995: { - ERR_EXPLAIN("RGBA float texture not supported"); - ERR_FAIL_V(image); + r_gl_internal_format = GL_RGB; + r_gl_format = GL_RGB; + r_gl_type = GL_UNSIGNED_BYTE; + + if (image.is_valid()) + + image = image->rgbe_to_srgb(); + + return image; } break; case Image::FORMAT_DXT1: { - need_decompress = true; + r_compressed = true; + if (config.s3tc_supported) { + r_gl_internal_format = _EXT_COMPRESSED_RGBA_S3TC_DXT1_EXT; + r_gl_format = GL_RGBA; + r_gl_type = GL_UNSIGNED_BYTE; + } else { + need_decompress = true; + } } break; case Image::FORMAT_DXT3: { - need_decompress = true; + if (config.s3tc_supported) { + r_gl_internal_format = _EXT_COMPRESSED_RGBA_S3TC_DXT3_EXT; + r_gl_format = GL_RGBA; + r_gl_type = GL_UNSIGNED_BYTE; + r_compressed = true; + } else { + need_decompress = true; + } } break; case Image::FORMAT_DXT5: { - need_decompress = true; + if (config.s3tc_supported) { + r_gl_internal_format = _EXT_COMPRESSED_RGBA_S3TC_DXT5_EXT; + r_gl_format = GL_RGBA; + r_gl_type = GL_UNSIGNED_BYTE; + r_compressed = true; + } else { + need_decompress = true; + } } break; case Image::FORMAT_RGTC_R: { @@ -198,7 +263,14 @@ Ref<Image> RasterizerStorageGLES2::_get_gl_image_and_format(const Ref<Image> &p_ } break; case Image::FORMAT_ETC: { - need_decompress = true; + if (config.etc1_supported) { + r_gl_internal_format = _EXT_ETC1_RGB8_OES; + r_gl_format = GL_RGBA; + r_gl_type = GL_UNSIGNED_BYTE; + r_compressed = true; + } else { + need_decompress = true; + } } break; case Image::FORMAT_ETC2_R11: { @@ -246,6 +318,7 @@ Ref<Image> RasterizerStorageGLES2::_get_gl_image_and_format(const Ref<Image> &p_ r_gl_format = GL_RGBA; r_gl_internal_format = GL_RGBA; r_gl_type = GL_UNSIGNED_BYTE; + r_real_format = Image::FORMAT_RGBA8; return image; } @@ -275,11 +348,14 @@ RID RasterizerStorageGLES2::texture_create() { return texture_owner.make_rid(texture); } -void RasterizerStorageGLES2::texture_allocate(RID p_texture, int p_width, int p_height, Image::Format p_format, uint32_t p_flags) { +void RasterizerStorageGLES2::texture_allocate(RID p_texture, int p_width, int p_height, int p_depth_3d, Image::Format p_format, VisualServer::TextureType p_type, uint32_t p_flags) { GLenum format; GLenum internal_format; GLenum type; + bool compressed = false; + bool srgb = false; + if (p_flags & VS::TEXTURE_FLAG_USED_FOR_STREAMING) { p_flags &= ~VS::TEXTURE_FLAG_MIPMAPS; // no mipies for video } @@ -291,9 +367,31 @@ void RasterizerStorageGLES2::texture_allocate(RID p_texture, int p_width, int p_ texture->format = p_format; texture->flags = p_flags; texture->stored_cube_sides = 0; - texture->target = (p_flags & VS::TEXTURE_FLAG_CUBEMAP) ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D; + texture->type = p_type; + + switch (p_type) { + case VS::TEXTURE_TYPE_2D: { + texture->target = GL_TEXTURE_2D; + texture->images.resize(1); + } break; + case VS::TEXTURE_TYPE_CUBEMAP: { + texture->target = GL_TEXTURE_CUBE_MAP; + texture->images.resize(6); + } break; + case VS::TEXTURE_TYPE_2D_ARRAY: { + texture->images.resize(p_depth_3d); + } break; + case VS::TEXTURE_TYPE_3D: { + texture->images.resize(p_depth_3d); + } break; + default: { + ERR_PRINT("Unknown texture type!"); + return; + } + } - _get_gl_image_and_format(Ref<Image>(), texture->format, texture->flags, format, internal_format, type); + Image::Format real_format; + _get_gl_image_and_format(Ref<Image>(), texture->format, texture->flags, real_format, format, internal_format, type, compressed); texture->alloc_width = texture->width; texture->alloc_height = texture->height; @@ -304,6 +402,8 @@ void RasterizerStorageGLES2::texture_allocate(RID p_texture, int p_width, int p_ texture->data_size = 0; texture->mipmaps = 1; + texture->compressed = compressed; + glActiveTexture(GL_TEXTURE0); glBindTexture(texture->target, texture->tex_id); @@ -315,7 +415,7 @@ void RasterizerStorageGLES2::texture_allocate(RID p_texture, int p_width, int p_ texture->active = true; } -void RasterizerStorageGLES2::texture_set_data(RID p_texture, const Ref<Image> &p_image, VS::CubeMapSide p_cube_side) { +void RasterizerStorageGLES2::texture_set_data(RID p_texture, const Ref<Image> &p_image, int p_layer) { Texture *texture = texture_owner.getornull(p_texture); ERR_FAIL_COND(!texture); @@ -328,13 +428,13 @@ void RasterizerStorageGLES2::texture_set_data(RID p_texture, const Ref<Image> &p GLenum format; GLenum internal_format; bool compressed = false; - bool srgb; if (config.keep_original_textures && !(texture->flags & VS::TEXTURE_FLAG_USED_FOR_STREAMING)) { - texture->images[p_cube_side] = p_image; + texture->images.write[p_layer] = p_image; } - Ref<Image> img = _get_gl_image_and_format(p_image, p_image->get_format(), texture->flags, format, internal_format, type); + Image::Format real_format; + Ref<Image> img = _get_gl_image_and_format(p_image, p_image->get_format(), texture->flags, real_format, format, internal_format, type, compressed); if (config.shrink_textures_x2 && (p_image->has_mipmaps() || !p_image->is_compressed()) && !(texture->flags & VS::TEXTURE_FLAG_USED_FOR_STREAMING)) { @@ -350,7 +450,7 @@ void RasterizerStorageGLES2::texture_set_data(RID p_texture, const Ref<Image> &p } }; - GLenum blit_target = (texture->target == GL_TEXTURE_CUBE_MAP) ? _cube_side_enum[p_cube_side] : GL_TEXTURE_2D; + GLenum blit_target = (texture->target == GL_TEXTURE_CUBE_MAP) ? _cube_side_enum[p_layer] : GL_TEXTURE_2D; texture->data_size = img->get_data().size(); PoolVector<uint8_t>::Read read = img->get_data().read(); @@ -423,11 +523,21 @@ void RasterizerStorageGLES2::texture_set_data(RID p_texture, const Ref<Image> &p int size, ofs; img->get_mipmap_offset_and_size(i, ofs, size); - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - if (texture->flags & VS::TEXTURE_FLAG_USED_FOR_STREAMING) { - glTexSubImage2D(blit_target, i, 0, 0, w, h, format, type, &read[ofs]); + if (texture->compressed) { + glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + + int bw = w; + int bh = h; + + glCompressedTexImage2D(blit_target, i, internal_format, bw, bh, 0, size, &read[ofs]); } else { - glTexImage2D(blit_target, i, internal_format, w, h, 0, format, type, &read[ofs]); + + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + if (texture->flags & VS::TEXTURE_FLAG_USED_FOR_STREAMING) { + glTexSubImage2D(blit_target, i, 0, 0, w, h, format, type, &read[ofs]); + } else { + glTexImage2D(blit_target, i, internal_format, w, h, 0, format, type, &read[ofs]); + } } tsize += size; @@ -442,9 +552,9 @@ void RasterizerStorageGLES2::texture_set_data(RID p_texture, const Ref<Image> &p // printf("texture: %i x %i - size: %i - total: %i\n", texture->width, texture->height, tsize, info.texture_mem); - texture->stored_cube_sides |= (1 << p_cube_side); + texture->stored_cube_sides |= (1 << p_layer); - if ((texture->flags & VS::TEXTURE_FLAG_MIPMAPS) && mipmaps == 1 && !texture->ignore_mipmaps && (!(texture->flags & VS::TEXTURE_FLAG_CUBEMAP) || texture->stored_cube_sides == (1 << 6) - 1)) { + if ((texture->flags & VS::TEXTURE_FLAG_MIPMAPS) && mipmaps == 1 && !texture->ignore_mipmaps && (texture->type != VS::TEXTURE_TYPE_CUBEMAP || texture->stored_cube_sides == (1 << 6) - 1)) { //generate mipmaps if they were requested and the image does not contain them glGenerateMipmap(texture->target); } @@ -452,12 +562,12 @@ void RasterizerStorageGLES2::texture_set_data(RID p_texture, const Ref<Image> &p texture->mipmaps = mipmaps; } -void RasterizerStorageGLES2::texture_set_data_partial(RID p_texture, const Ref<Image> &p_image, int src_x, int src_y, int src_w, int src_h, int dst_x, int dst_y, int p_dst_mip, VS::CubeMapSide p_cube_side) { +void RasterizerStorageGLES2::texture_set_data_partial(RID p_texture, const Ref<Image> &p_image, int src_x, int src_y, int src_w, int src_h, int dst_x, int dst_y, int p_dst_mip, int p_layer) { // TODO ERR_PRINT("Not implemented (ask Karroffel to do it :p)"); } -Ref<Image> RasterizerStorageGLES2::texture_get_data(RID p_texture, VS::CubeMapSide p_cube_side) const { +Ref<Image> RasterizerStorageGLES2::texture_get_data(RID p_texture, int p_layer) const { Texture *texture = texture_owner.getornull(p_texture); @@ -465,14 +575,22 @@ Ref<Image> RasterizerStorageGLES2::texture_get_data(RID p_texture, VS::CubeMapSi ERR_FAIL_COND_V(!texture->active, Ref<Image>()); ERR_FAIL_COND_V(texture->data_size == 0 && !texture->render_target, Ref<Image>()); - if (!texture->images[p_cube_side].is_null()) { - return texture->images[p_cube_side]; + if (texture->type == VS::TEXTURE_TYPE_CUBEMAP && p_layer < 6 && p_layer >= 0 && !texture->images[p_layer].is_null()) { + return texture->images[p_layer]; } + #ifdef GLES_OVER_GL + Image::Format real_format; + GLenum gl_format; + GLenum gl_internal_format; + GLenum gl_type; + bool compressed; + _get_gl_image_and_format(Ref<Image>(), texture->format, texture->flags, real_format, gl_format, gl_internal_format, gl_type, compressed); + PoolVector<uint8_t> data; - int data_size = Image::get_image_data_size(texture->alloc_width, texture->alloc_height, texture->format, texture->mipmaps > 1 ? -1 : 0); + int data_size = Image::get_image_data_size(texture->alloc_width, texture->alloc_height, real_format, texture->mipmaps > 1 ? -1 : 0); data.resize(data_size * 2); //add some memory at the end, just in case for buggy drivers PoolVector<uint8_t>::Write wb = data.write(); @@ -489,19 +607,23 @@ Ref<Image> RasterizerStorageGLES2::texture_get_data(RID p_texture, VS::CubeMapSi int ofs = 0; if (i > 0) { - ofs = Image::get_image_data_size(texture->alloc_width, texture->alloc_height, texture->format, i - 1); + ofs = Image::get_image_data_size(texture->alloc_width, texture->alloc_height, real_format, i - 1); } - glPixelStorei(GL_PACK_ALIGNMENT, 1); - - glGetTexImage(texture->target, i, texture->gl_format_cache, texture->gl_type_cache, &wb[ofs]); + if (texture->compressed) { + glPixelStorei(GL_PACK_ALIGNMENT, 4); + glGetCompressedTexImage(texture->target, i, &wb[ofs]); + } else { + glPixelStorei(GL_PACK_ALIGNMENT, 1); + glGetTexImage(texture->target, i, texture->gl_format_cache, texture->gl_type_cache, &wb[ofs]); + } } wb = PoolVector<uint8_t>::Write(); data.resize(data_size); - Image *img = memnew(Image(texture->alloc_width, texture->alloc_height, texture->mipmaps > 1 ? true : false, texture->format, data)); + Image *img = memnew(Image(texture->alloc_width, texture->alloc_height, texture->mipmaps > 1 ? true : false, real_format, data)); return Ref<Image>(img); #else @@ -518,10 +640,10 @@ void RasterizerStorageGLES2::texture_set_flags(RID p_texture, uint32_t p_flags) bool had_mipmaps = texture->flags & VS::TEXTURE_FLAG_MIPMAPS; + texture->flags = p_flags; + glActiveTexture(GL_TEXTURE0); glBindTexture(texture->target, texture->tex_id); - uint32_t cube = texture->flags & VS::TEXTURE_FLAG_CUBEMAP; - texture->flags = p_flags | cube; // can't remove a cube from being a cube if (((texture->flags & VS::TEXTURE_FLAG_REPEAT) || (texture->flags & VS::TEXTURE_FLAG_MIRRORED_REPEAT)) && texture->target != GL_TEXTURE_CUBE_MAP) { @@ -578,6 +700,14 @@ Image::Format RasterizerStorageGLES2::texture_get_format(RID p_texture) const { return texture->format; } +VisualServer::TextureType RasterizerStorageGLES2::texture_get_type(RID p_texture) const { + Texture *texture = texture_owner.getornull(p_texture); + + ERR_FAIL_COND_V(!texture, VS::TEXTURE_TYPE_2D); + + return texture->type; +} + uint32_t RasterizerStorageGLES2::texture_get_texid(RID p_texture) const { Texture *texture = texture_owner.getornull(p_texture); @@ -602,7 +732,15 @@ uint32_t RasterizerStorageGLES2::texture_get_height(RID p_texture) const { return texture->height; } -void RasterizerStorageGLES2::texture_set_size_override(RID p_texture, int p_width, int p_height) { +uint32_t RasterizerStorageGLES2::texture_get_depth(RID p_texture) const { + Texture *texture = texture_owner.getornull(p_texture); + + ERR_FAIL_COND_V(!texture, 0); + + return texture->depth; +} + +void RasterizerStorageGLES2::texture_set_size_override(RID p_texture, int p_width, int p_height, int p_depth) { Texture *texture = texture_owner.getornull(p_texture); ERR_FAIL_COND(!texture); @@ -641,8 +779,9 @@ void RasterizerStorageGLES2::texture_debug_usage(List<VS::TextureInfo> *r_info) VS::TextureInfo tinfo; tinfo.path = t->path; tinfo.format = t->format; - tinfo.size.x = t->alloc_width; - tinfo.size.y = t->alloc_height; + tinfo.width = t->alloc_width; + tinfo.height = t->alloc_height; + tinfo.depth = 0; tinfo.bytes = t->total_data_size; r_info->push_back(tinfo); } @@ -674,28 +813,180 @@ void RasterizerStorageGLES2::texture_set_proxy(RID p_texture, RID p_proxy) { } } +void RasterizerStorageGLES2::texture_set_force_redraw_if_visible(RID p_texture, bool p_enable) { + + Texture *texture = texture_owner.getornull(p_texture); + ERR_FAIL_COND(!texture); + + texture->redraw_if_visible = p_enable; +} + void RasterizerStorageGLES2::texture_set_detect_3d_callback(RID p_texture, VisualServer::TextureDetectCallback p_callback, void *p_userdata) { - // TODO + Texture *texture = texture_owner.get(p_texture); + ERR_FAIL_COND(!texture); + + texture->detect_3d = p_callback; + texture->detect_3d_ud = p_userdata; } void RasterizerStorageGLES2::texture_set_detect_srgb_callback(RID p_texture, VisualServer::TextureDetectCallback p_callback, void *p_userdata) { - // TODO + Texture *texture = texture_owner.get(p_texture); + ERR_FAIL_COND(!texture); + + texture->detect_srgb = p_callback; + texture->detect_srgb_ud = p_userdata; } void RasterizerStorageGLES2::texture_set_detect_normal_callback(RID p_texture, VisualServer::TextureDetectCallback p_callback, void *p_userdata) { - // TODO + Texture *texture = texture_owner.get(p_texture); + ERR_FAIL_COND(!texture); + + texture->detect_normal = p_callback; + texture->detect_normal_ud = p_userdata; } RID RasterizerStorageGLES2::texture_create_radiance_cubemap(RID p_source, int p_resolution) const { - // TODO + return RID(); } RID RasterizerStorageGLES2::sky_create() { - return RID(); + Sky *sky = memnew(Sky); + sky->radiance = 0; + return sky_owner.make_rid(sky); } void RasterizerStorageGLES2::sky_set_texture(RID p_sky, RID p_panorama, int p_radiance_size) { + Sky *sky = sky_owner.getornull(p_sky); + ERR_FAIL_COND(!sky); + + if (sky->panorama.is_valid()) { + sky->panorama = RID(); + glDeleteTextures(1, &sky->radiance); + sky->radiance = 0; + } + + sky->panorama = p_panorama; + if (!sky->panorama.is_valid()) { + return; // the panorama was cleared + } + + Texture *texture = texture_owner.getornull(sky->panorama); + if (!texture) { + sky->panorama = RID(); + ERR_FAIL_COND(!texture); + } + + // glBindVertexArray(0) and more + { + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glDisable(GL_CULL_FACE); + glDisable(GL_DEPTH_TEST); + glDisable(GL_SCISSOR_TEST); + glDisable(GL_BLEND); + + for (int i = 0; i < VS::ARRAY_MAX - 1; i++) { + glDisableVertexAttribArray(i); + } + } + + glActiveTexture(GL_TEXTURE0); + glBindTexture(texture->target, texture->tex_id); + + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); //need this for proper sampling + + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, resources.radical_inverse_vdc_cache_tex); + + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + // New cubemap that will hold the mipmaps with different roughness values + glActiveTexture(GL_TEXTURE2); + glGenTextures(1, &sky->radiance); + glBindTexture(GL_TEXTURE_CUBE_MAP, sky->radiance); + + // Now we create a new framebuffer. The new cubemap images will be used as + // attachements for it, so we can fill them by issuing draw calls. + GLuint tmp_fb; + + glGenFramebuffers(1, &tmp_fb); + glBindFramebuffer(GL_FRAMEBUFFER, tmp_fb); + + int size = p_radiance_size; + + int lod = 0; + + shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES2::USE_SOURCE_PANORAMA, texture->target == GL_TEXTURE_2D); + + shaders.cubemap_filter.bind(); + + int mipmaps = 6; + + int mm_level = mipmaps; + + GLenum internal_format = GL_RGBA; + GLenum format = GL_RGBA; + GLenum type = GL_UNSIGNED_BYTE; // This is suboptimal... TODO other format for FBO? + + // Set the initial (empty) mipmaps + while (size >= 1) { + + for (int i = 0; i < 6; i++) { + glTexImage2D(_cube_side_enum[i], lod, internal_format, size, size, 0, format, type, NULL); + } + + lod++; + + size >>= 1; + } + + lod = 0; + mm_level = mipmaps; + + size = p_radiance_size; + + // now render to the framebuffer, mipmap level for mipmap level + while (size >= 1) { + + for (int i = 0; i < 6; i++) { + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, _cube_side_enum[i], sky->radiance, lod); + + glViewport(0, 0, size, size); + + bind_quad_array(); + + shaders.cubemap_filter.set_uniform(CubemapFilterShaderGLES2::FACE_ID, i); + + float roughness = mm_level ? lod / (float)(mipmaps - 1) : 1; + shaders.cubemap_filter.set_uniform(CubemapFilterShaderGLES2::ROUGHNESS, roughness); + + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + } + + size >>= 1; + + mm_level--; + + lod++; + } + + // restore ranges + + glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + // Framebuffer did its job. thank mr framebuffer + glBindFramebuffer(GL_FRAMEBUFFER, RasterizerStorageGLES2::system_fbo); + glDeleteFramebuffers(1, &tmp_fb); } /* SHADER API */ @@ -747,6 +1038,8 @@ void RasterizerStorageGLES2::shader_set_code(RID p_shader, const String &p_code) if (mode == VS::SHADER_CANVAS_ITEM) { shader->shader = &canvas->state.canvas_shader; + } else if (mode == VS::SHADER_SPATIAL) { + shader->shader = &scene->state.scene_shader; } else { return; } @@ -807,6 +1100,62 @@ void RasterizerStorageGLES2::_update_shader(Shader *p_shader) const { actions->uniforms = &p_shader->uniforms; } break; + case VS::SHADER_SPATIAL: { + p_shader->spatial.blend_mode = Shader::Spatial::BLEND_MODE_MIX; + p_shader->spatial.depth_draw_mode = Shader::Spatial::DEPTH_DRAW_OPAQUE; + p_shader->spatial.cull_mode = Shader::Spatial::CULL_MODE_BACK; + p_shader->spatial.uses_alpha = false; + p_shader->spatial.uses_alpha_scissor = false; + p_shader->spatial.uses_discard = false; + p_shader->spatial.unshaded = false; + p_shader->spatial.no_depth_test = false; + p_shader->spatial.uses_sss = false; + p_shader->spatial.uses_time = false; + p_shader->spatial.uses_vertex_lighting = false; + p_shader->spatial.uses_screen_texture = false; + p_shader->spatial.uses_depth_texture = false; + p_shader->spatial.uses_vertex = false; + p_shader->spatial.writes_modelview_or_projection = false; + p_shader->spatial.uses_world_coordinates = false; + + shaders.actions_scene.render_mode_values["blend_add"] = Pair<int *, int>(&p_shader->spatial.blend_mode, Shader::Spatial::BLEND_MODE_ADD); + shaders.actions_scene.render_mode_values["blend_mix"] = Pair<int *, int>(&p_shader->spatial.blend_mode, Shader::Spatial::BLEND_MODE_MIX); + shaders.actions_scene.render_mode_values["blend_sub"] = Pair<int *, int>(&p_shader->spatial.blend_mode, Shader::Spatial::BLEND_MODE_SUB); + shaders.actions_scene.render_mode_values["blend_mul"] = Pair<int *, int>(&p_shader->spatial.blend_mode, Shader::Spatial::BLEND_MODE_MUL); + + shaders.actions_scene.render_mode_values["depth_draw_opaque"] = Pair<int *, int>(&p_shader->spatial.depth_draw_mode, Shader::Spatial::DEPTH_DRAW_OPAQUE); + shaders.actions_scene.render_mode_values["depth_draw_always"] = Pair<int *, int>(&p_shader->spatial.depth_draw_mode, Shader::Spatial::DEPTH_DRAW_ALWAYS); + shaders.actions_scene.render_mode_values["depth_draw_never"] = Pair<int *, int>(&p_shader->spatial.depth_draw_mode, Shader::Spatial::DEPTH_DRAW_NEVER); + shaders.actions_scene.render_mode_values["depth_draw_alpha_prepass"] = Pair<int *, int>(&p_shader->spatial.depth_draw_mode, Shader::Spatial::DEPTH_DRAW_ALPHA_PREPASS); + + shaders.actions_scene.render_mode_values["cull_front"] = Pair<int *, int>(&p_shader->spatial.cull_mode, Shader::Spatial::CULL_MODE_FRONT); + shaders.actions_scene.render_mode_values["cull_back"] = Pair<int *, int>(&p_shader->spatial.cull_mode, Shader::Spatial::CULL_MODE_BACK); + shaders.actions_scene.render_mode_values["cull_disabled"] = Pair<int *, int>(&p_shader->spatial.cull_mode, Shader::Spatial::CULL_MODE_DISABLED); + + shaders.actions_scene.render_mode_flags["unshaded"] = &p_shader->spatial.unshaded; + shaders.actions_scene.render_mode_flags["depth_test_disable"] = &p_shader->spatial.no_depth_test; + + shaders.actions_scene.render_mode_flags["vertex_lighting"] = &p_shader->spatial.uses_vertex_lighting; + + shaders.actions_scene.render_mode_flags["world_vertex_coords"] = &p_shader->spatial.uses_world_coordinates; + + shaders.actions_scene.usage_flag_pointers["ALPHA"] = &p_shader->spatial.uses_alpha; + shaders.actions_scene.usage_flag_pointers["ALPHA_SCISSOR"] = &p_shader->spatial.uses_alpha_scissor; + + shaders.actions_scene.usage_flag_pointers["SSS_STRENGTH"] = &p_shader->spatial.uses_sss; + shaders.actions_scene.usage_flag_pointers["DISCARD"] = &p_shader->spatial.uses_discard; + shaders.actions_scene.usage_flag_pointers["SCREEN_TEXTURE"] = &p_shader->spatial.uses_screen_texture; + shaders.actions_scene.usage_flag_pointers["DEPTH_TEXTURE"] = &p_shader->spatial.uses_depth_texture; + shaders.actions_scene.usage_flag_pointers["TIME"] = &p_shader->spatial.uses_time; + + shaders.actions_scene.write_flag_pointers["MODELVIEW_MATRIX"] = &p_shader->spatial.writes_modelview_or_projection; + shaders.actions_scene.write_flag_pointers["PROJECTION_MATRIX"] = &p_shader->spatial.writes_modelview_or_projection; + shaders.actions_scene.write_flag_pointers["VERTEX"] = &p_shader->spatial.uses_vertex; + + actions = &shaders.actions_scene; + actions->uniforms = &p_shader->uniforms; + } break; + default: { return; } break; @@ -824,6 +1173,11 @@ void RasterizerStorageGLES2::_update_shader(Shader *p_shader) const { p_shader->uses_vertex_time = gen_code.uses_vertex_time; p_shader->uses_fragment_time = gen_code.uses_fragment_time; + p_shader->shader->set_custom_shader(p_shader->custom_code_id); + p_shader->shader->bind(); + + // cache uniform locations + for (SelfList<Material> *E = p_shader->materials.first(); E; E = E->next()) { _material_make_dirty(E->self()); } @@ -911,6 +1265,10 @@ void RasterizerStorageGLES2::shader_get_param_list(RID p_shader, List<PropertyIn pi.type = Variant::POOL_INT_ARRAY; } break; + case ShaderLanguage::TYPE_FLOAT: { + pi.type = Variant::REAL; + } break; + case ShaderLanguage::TYPE_VEC2: { pi.type = Variant::VECTOR2; } break; @@ -951,6 +1309,10 @@ void RasterizerStorageGLES2::shader_get_param_list(RID p_shader, List<PropertyIn pi.hint = PROPERTY_HINT_RESOURCE_TYPE; pi.hint_string = "CubeMap"; } break; + + default: { + + } break; } p_param_list->push_back(pi); @@ -1063,347 +1425,1864 @@ Variant RasterizerStorageGLES2::material_get_param(RID p_material, const StringN } void RasterizerStorageGLES2::material_set_line_width(RID p_material, float p_width) { + Material *material = material_owner.getornull(p_material); + ERR_FAIL_COND(!material); + + material->line_width = p_width; } void RasterizerStorageGLES2::material_set_next_pass(RID p_material, RID p_next_material) { + Material *material = material_owner.get(p_material); + ERR_FAIL_COND(!material); + + material->next_pass = p_next_material; } bool RasterizerStorageGLES2::material_is_animated(RID p_material) { - return false; + Material *material = material_owner.get(p_material); + ERR_FAIL_COND_V(!material, false); + if (material->dirty_list.in_list()) { + _update_material(material); + } + + bool animated = material->is_animated_cache; + if (!animated && material->next_pass.is_valid()) { + animated = material_is_animated(material->next_pass); + } + return animated; } bool RasterizerStorageGLES2::material_casts_shadows(RID p_material) { - return false; + Material *material = material_owner.get(p_material); + ERR_FAIL_COND_V(!material, false); + if (material->dirty_list.in_list()) { + _update_material(material); + } + + bool casts_shadows = material->can_cast_shadow_cache; + + if (!casts_shadows && material->next_pass.is_valid()) { + casts_shadows = material_casts_shadows(material->next_pass); + } + + return casts_shadows; } void RasterizerStorageGLES2::material_add_instance_owner(RID p_material, RasterizerScene::InstanceBase *p_instance) { + + Material *material = material_owner.getornull(p_material); + ERR_FAIL_COND(!material); + + Map<RasterizerScene::InstanceBase *, int>::Element *E = material->instance_owners.find(p_instance); + if (E) { + E->get()++; + } else { + material->instance_owners[p_instance] = 1; + } } void RasterizerStorageGLES2::material_remove_instance_owner(RID p_material, RasterizerScene::InstanceBase *p_instance) { + + Material *material = material_owner.getornull(p_material); + ERR_FAIL_COND(!material); + + Map<RasterizerScene::InstanceBase *, int>::Element *E = material->instance_owners.find(p_instance); + ERR_FAIL_COND(!E); + + E->get()--; + + if (E->get() == 0) { + material->instance_owners.erase(E); + } } void RasterizerStorageGLES2::material_set_render_priority(RID p_material, int priority) { + ERR_FAIL_COND(priority < VS::MATERIAL_RENDER_PRIORITY_MIN); + ERR_FAIL_COND(priority > VS::MATERIAL_RENDER_PRIORITY_MAX); + + Material *material = material_owner.get(p_material); + ERR_FAIL_COND(!material); + + material->render_priority = priority; +} + +void RasterizerStorageGLES2::_update_material(Material *p_material) { + if (p_material->dirty_list.in_list()) { + _material_dirty_list.remove(&p_material->dirty_list); + } + + if (p_material->shader && p_material->shader->dirty_list.in_list()) { + _update_shader(p_material->shader); + } + + if (p_material->shader && !p_material->shader->valid) { + return; + } + + { + bool can_cast_shadow = false; + bool is_animated = false; + + if (p_material->shader && p_material->shader->mode == VS::SHADER_SPATIAL) { + + if (p_material->shader->spatial.blend_mode == Shader::Spatial::BLEND_MODE_MIX && + (!p_material->shader->spatial.uses_alpha || (p_material->shader->spatial.uses_alpha && p_material->shader->spatial.depth_draw_mode == Shader::Spatial::DEPTH_DRAW_ALPHA_PREPASS))) { + can_cast_shadow = true; + } + + if (p_material->shader->spatial.uses_discard && p_material->shader->uses_fragment_time) { + is_animated = true; + } + + if (p_material->shader->spatial.uses_vertex && p_material->shader->uses_vertex_time) { + is_animated = true; + } + + if (can_cast_shadow != p_material->can_cast_shadow_cache || is_animated != p_material->is_animated_cache) { + p_material->can_cast_shadow_cache = can_cast_shadow; + p_material->is_animated_cache = is_animated; + + for (Map<Geometry *, int>::Element *E = p_material->geometry_owners.front(); E; E = E->next()) { + E->key()->material_changed_notify(); + } + + for (Map<RasterizerScene::InstanceBase *, int>::Element *E = p_material->instance_owners.front(); E; E = E->next()) { + E->key()->base_material_changed(); + } + } + } + } + + // uniforms and other thigns will be set in the use_material method in ShaderGLES2 + + if (p_material->shader && p_material->shader->texture_count > 0) { + + p_material->textures.resize(p_material->shader->texture_count); + + for (Map<StringName, ShaderLanguage::ShaderNode::Uniform>::Element *E = p_material->shader->uniforms.front(); E; E = E->next()) { + if (E->get().texture_order < 0) + continue; // not a texture, does not go here + + RID texture; + + Map<StringName, Variant>::Element *V = p_material->params.find(E->key()); + + if (V) { + texture = V->get(); + } + + if (!texture.is_valid()) { + Map<StringName, RID>::Element *W = p_material->shader->default_textures.find(E->key()); + + if (W) { + texture = W->get(); + } + } + + p_material->textures.write[E->get().texture_order] = Pair<StringName, RID>(E->key(), texture); + } + } else { + p_material->textures.clear(); + } +} + +void RasterizerStorageGLES2::_material_add_geometry(RID p_material, Geometry *p_geometry) { + Material *material = material_owner.getornull(p_material); + ERR_FAIL_COND(!material); + + Map<Geometry *, int>::Element *I = material->geometry_owners.find(p_geometry); + + if (I) { + I->get()++; + } else { + material->geometry_owners[p_geometry] = 1; + } +} + +void RasterizerStorageGLES2::_material_remove_geometry(RID p_material, Geometry *p_geometry) { + + Material *material = material_owner.getornull(p_material); + ERR_FAIL_COND(!material); + + Map<Geometry *, int>::Element *I = material->geometry_owners.find(p_geometry); + ERR_FAIL_COND(!I); + + I->get()--; + + if (I->get() == 0) { + material->geometry_owners.erase(I); + } } void RasterizerStorageGLES2::update_dirty_materials() { + while (_material_dirty_list.first()) { + + Material *material = _material_dirty_list.first()->self(); + _update_material(material); + } } /* MESH API */ RID RasterizerStorageGLES2::mesh_create() { - return RID(); + + Mesh *mesh = memnew(Mesh); + + return mesh_owner.make_rid(mesh); } void RasterizerStorageGLES2::mesh_add_surface(RID p_mesh, uint32_t p_format, VS::PrimitiveType p_primitive, const PoolVector<uint8_t> &p_array, int p_vertex_count, const PoolVector<uint8_t> &p_index_array, int p_index_count, const AABB &p_aabb, const Vector<PoolVector<uint8_t> > &p_blend_shapes, const Vector<AABB> &p_bone_aabbs) { + PoolVector<uint8_t> array = p_array; + + Mesh *mesh = mesh_owner.getornull(p_mesh); + ERR_FAIL_COND(!mesh); + + ERR_FAIL_COND(!(p_format & VS::ARRAY_FORMAT_VERTEX)); + + //must have index and bones, both. + { + uint32_t bones_weight = VS::ARRAY_FORMAT_BONES | VS::ARRAY_FORMAT_WEIGHTS; + ERR_EXPLAIN("Array must have both bones and weights in format or none."); + ERR_FAIL_COND((p_format & bones_weight) && (p_format & bones_weight) != bones_weight); + } + + //bool has_morph = p_blend_shapes.size(); + + Surface::Attrib attribs[VS::ARRAY_MAX]; + + int stride = 0; + + for (int i = 0; i < VS::ARRAY_MAX; i++) { + + attribs[i].index = i; + + if (!(p_format & (1 << i))) { + attribs[i].enabled = false; + attribs[i].integer = false; + continue; + } + + attribs[i].enabled = true; + attribs[i].offset = stride; + attribs[i].integer = false; + + switch (i) { + + case VS::ARRAY_VERTEX: { + + if (p_format & VS::ARRAY_FLAG_USE_2D_VERTICES) { + attribs[i].size = 2; + } else { + attribs[i].size = (p_format & VS::ARRAY_COMPRESS_VERTEX) ? 4 : 3; + } + + if (p_format & VS::ARRAY_COMPRESS_VERTEX) { + attribs[i].type = _GL_HALF_FLOAT_OES; + stride += attribs[i].size * 2; + } else { + attribs[i].type = GL_FLOAT; + stride += attribs[i].size * 4; + } + + attribs[i].normalized = GL_FALSE; + + } break; + case VS::ARRAY_NORMAL: { + + attribs[i].size = 3; + + if (p_format & VS::ARRAY_COMPRESS_NORMAL) { + attribs[i].type = GL_BYTE; + stride += 4; //pad extra byte + attribs[i].normalized = GL_TRUE; + } else { + attribs[i].type = GL_FLOAT; + stride += 12; + attribs[i].normalized = GL_FALSE; + } + + } break; + case VS::ARRAY_TANGENT: { + + attribs[i].size = 4; + + if (p_format & VS::ARRAY_COMPRESS_TANGENT) { + attribs[i].type = GL_BYTE; + stride += 4; + attribs[i].normalized = GL_TRUE; + } else { + attribs[i].type = GL_FLOAT; + stride += 16; + attribs[i].normalized = GL_FALSE; + } + + } break; + case VS::ARRAY_COLOR: { + + attribs[i].size = 4; + + if (p_format & VS::ARRAY_COMPRESS_COLOR) { + attribs[i].type = GL_UNSIGNED_BYTE; + stride += 4; + attribs[i].normalized = GL_TRUE; + } else { + attribs[i].type = GL_FLOAT; + stride += 16; + attribs[i].normalized = GL_FALSE; + } + + } break; + case VS::ARRAY_TEX_UV: { + + attribs[i].size = 2; + + if (p_format & VS::ARRAY_COMPRESS_TEX_UV) { + attribs[i].type = _GL_HALF_FLOAT_OES; + stride += 4; + } else { + attribs[i].type = GL_FLOAT; + stride += 8; + } + + attribs[i].normalized = GL_FALSE; + + } break; + case VS::ARRAY_TEX_UV2: { + + attribs[i].size = 2; + + if (p_format & VS::ARRAY_COMPRESS_TEX_UV2) { + attribs[i].type = _GL_HALF_FLOAT_OES; + stride += 4; + } else { + attribs[i].type = GL_FLOAT; + stride += 8; + } + attribs[i].normalized = GL_FALSE; + + } break; + case VS::ARRAY_BONES: { + + attribs[i].size = 4; + + if (p_format & VS::ARRAY_FLAG_USE_16_BIT_BONES) { + attribs[i].type = GL_UNSIGNED_SHORT; + stride += 8; + } else { + attribs[i].type = GL_UNSIGNED_BYTE; + stride += 4; + } + + attribs[i].normalized = GL_FALSE; + attribs[i].integer = true; + + } break; + case VS::ARRAY_WEIGHTS: { + + attribs[i].size = 4; + + if (p_format & VS::ARRAY_COMPRESS_WEIGHTS) { + + attribs[i].type = GL_UNSIGNED_SHORT; + stride += 8; + attribs[i].normalized = GL_TRUE; + } else { + attribs[i].type = GL_FLOAT; + stride += 16; + attribs[i].normalized = GL_FALSE; + } + + } break; + case VS::ARRAY_INDEX: { + + attribs[i].size = 1; + + if (p_vertex_count >= (1 << 16)) { + attribs[i].type = GL_UNSIGNED_INT; + attribs[i].stride = 4; + } else { + attribs[i].type = GL_UNSIGNED_SHORT; + attribs[i].stride = 2; + } + + attribs[i].normalized = GL_FALSE; + + } break; + } + } + + for (int i = 0; i < VS::ARRAY_MAX - 1; i++) { + attribs[i].stride = stride; + } + + //validate sizes + + int array_size = stride * p_vertex_count; + int index_array_size = 0; + if (array.size() != array_size && array.size() + p_vertex_count * 2 == array_size) { + //old format, convert + array = PoolVector<uint8_t>(); + + array.resize(p_array.size() + p_vertex_count * 2); + + PoolVector<uint8_t>::Write w = array.write(); + PoolVector<uint8_t>::Read r = p_array.read(); + + uint16_t *w16 = (uint16_t *)w.ptr(); + const uint16_t *r16 = (uint16_t *)r.ptr(); + + uint16_t one = Math::make_half_float(1); + + for (int i = 0; i < p_vertex_count; i++) { + + *w16++ = *r16++; + *w16++ = *r16++; + *w16++ = *r16++; + *w16++ = one; + for (int j = 0; j < (stride / 2) - 4; j++) { + *w16++ = *r16++; + } + } + } + + ERR_FAIL_COND(array.size() != array_size); + + if (p_format & VS::ARRAY_FORMAT_INDEX) { + + index_array_size = attribs[VS::ARRAY_INDEX].stride * p_index_count; + } + + ERR_FAIL_COND(p_index_array.size() != index_array_size); + + ERR_FAIL_COND(p_blend_shapes.size() != mesh->blend_shape_count); + + for (int i = 0; i < p_blend_shapes.size(); i++) { + ERR_FAIL_COND(p_blend_shapes[i].size() != array_size); + } + + // all valid, create stuff + + Surface *surface = memnew(Surface); + + surface->active = true; + surface->array_len = p_vertex_count; + surface->index_array_len = p_index_count; + surface->array_byte_size = array.size(); + surface->index_array_byte_size = p_index_array.size(); + surface->primitive = p_primitive; + surface->mesh = mesh; + surface->format = p_format; + surface->skeleton_bone_aabb = p_bone_aabbs; + surface->skeleton_bone_used.resize(surface->skeleton_bone_aabb.size()); + + surface->aabb = p_aabb; + surface->max_bone = p_bone_aabbs.size(); + + surface->data = array; + surface->index_data = p_index_array; + + surface->total_data_size += surface->array_byte_size + surface->index_array_byte_size; + + for (int i = 0; i < surface->skeleton_bone_used.size(); i++) { + surface->skeleton_bone_used.write[i] = surface->skeleton_bone_aabb[i].size.x < 0 || surface->skeleton_bone_aabb[i].size.y < 0 || surface->skeleton_bone_aabb[i].size.z < 0; + } + + for (int i = 0; i < VS::ARRAY_MAX; i++) { + surface->attribs[i] = attribs[i]; + } + + // Okay, now the OpenGL stuff, wheeeeey \o/ + { + PoolVector<uint8_t>::Read vr = array.read(); + + glGenBuffers(1, &surface->vertex_id); + glBindBuffer(GL_ARRAY_BUFFER, surface->vertex_id); + glBufferData(GL_ARRAY_BUFFER, array_size, vr.ptr(), (p_format & VS::ARRAY_FLAG_USE_DYNAMIC_UPDATE) ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + + if (p_format & VS::ARRAY_FORMAT_INDEX) { + PoolVector<uint8_t>::Read ir = p_index_array.read(); + + glGenBuffers(1, &surface->index_id); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, surface->index_id); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, index_array_size, ir.ptr(), GL_STATIC_DRAW); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + } + + // TODO generate wireframes + } + + { + // blend shapes + + for (int i = 0; i < p_blend_shapes.size(); i++) { + + Surface::BlendShape mt; + + PoolVector<uint8_t>::Read vr = p_blend_shapes[i].read(); + + surface->total_data_size += array_size; + + glGenBuffers(1, &mt.vertex_id); + glBindBuffer(GL_ARRAY_BUFFER, mt.vertex_id); + glBufferData(GL_ARRAY_BUFFER, array_size, vr.ptr(), GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + surface->blend_shapes.push_back(mt); + } + } + + mesh->surfaces.push_back(surface); + mesh->instance_change_notify(); + + info.vertex_mem += surface->total_data_size; } void RasterizerStorageGLES2::mesh_set_blend_shape_count(RID p_mesh, int p_amount) { + Mesh *mesh = mesh_owner.getornull(p_mesh); + ERR_FAIL_COND(!mesh); + + ERR_FAIL_COND(mesh->surfaces.size() != 0); + ERR_FAIL_COND(p_amount < 0); + + mesh->blend_shape_count = p_amount; } int RasterizerStorageGLES2::mesh_get_blend_shape_count(RID p_mesh) const { - return 0; + const Mesh *mesh = mesh_owner.getornull(p_mesh); + ERR_FAIL_COND_V(!mesh, 0); + + return mesh->blend_shape_count; } void RasterizerStorageGLES2::mesh_set_blend_shape_mode(RID p_mesh, VS::BlendShapeMode p_mode) { + Mesh *mesh = mesh_owner.getornull(p_mesh); + ERR_FAIL_COND(!mesh); + + mesh->blend_shape_mode = p_mode; } VS::BlendShapeMode RasterizerStorageGLES2::mesh_get_blend_shape_mode(RID p_mesh) const { - return VS::BLEND_SHAPE_MODE_NORMALIZED; + const Mesh *mesh = mesh_owner.getornull(p_mesh); + ERR_FAIL_COND_V(!mesh, VS::BLEND_SHAPE_MODE_NORMALIZED); + + return mesh->blend_shape_mode; } void RasterizerStorageGLES2::mesh_surface_update_region(RID p_mesh, int p_surface, int p_offset, const PoolVector<uint8_t> &p_data) { + Mesh *mesh = mesh_owner.getornull(p_mesh); + + ERR_FAIL_COND(!mesh); + ERR_FAIL_INDEX(p_surface, mesh->surfaces.size()); + + int total_size = p_data.size(); + ERR_FAIL_COND(p_offset + total_size > mesh->surfaces[p_surface]->array_byte_size); + + PoolVector<uint8_t>::Read r = p_data.read(); + + glBindBuffer(GL_ARRAY_BUFFER, mesh->surfaces[p_surface]->vertex_id); + glBufferSubData(GL_ARRAY_BUFFER, p_offset, total_size, r.ptr()); + glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind } void RasterizerStorageGLES2::mesh_surface_set_material(RID p_mesh, int p_surface, RID p_material) { + Mesh *mesh = mesh_owner.getornull(p_mesh); + ERR_FAIL_COND(!mesh); + ERR_FAIL_INDEX(p_surface, mesh->surfaces.size()); + + if (mesh->surfaces[p_surface]->material == p_material) + return; + + if (mesh->surfaces[p_surface]->material.is_valid()) { + _material_remove_geometry(mesh->surfaces[p_surface]->material, mesh->surfaces[p_surface]); + } + + mesh->surfaces[p_surface]->material = p_material; + + if (mesh->surfaces[p_surface]->material.is_valid()) { + _material_add_geometry(mesh->surfaces[p_surface]->material, mesh->surfaces[p_surface]); + } + + mesh->instance_material_change_notify(); } RID RasterizerStorageGLES2::mesh_surface_get_material(RID p_mesh, int p_surface) const { - return RID(); + const Mesh *mesh = mesh_owner.getornull(p_mesh); + ERR_FAIL_COND_V(!mesh, RID()); + ERR_FAIL_INDEX_V(p_surface, mesh->surfaces.size(), RID()); + + return mesh->surfaces[p_surface]->material; } int RasterizerStorageGLES2::mesh_surface_get_array_len(RID p_mesh, int p_surface) const { - return 0; + const Mesh *mesh = mesh_owner.getornull(p_mesh); + ERR_FAIL_COND_V(!mesh, 0); + ERR_FAIL_INDEX_V(p_surface, mesh->surfaces.size(), 0); + + return mesh->surfaces[p_surface]->array_len; } int RasterizerStorageGLES2::mesh_surface_get_array_index_len(RID p_mesh, int p_surface) const { - return 0; + const Mesh *mesh = mesh_owner.getornull(p_mesh); + ERR_FAIL_COND_V(!mesh, 0); + ERR_FAIL_INDEX_V(p_surface, mesh->surfaces.size(), 0); + + return mesh->surfaces[p_surface]->index_array_len; } PoolVector<uint8_t> RasterizerStorageGLES2::mesh_surface_get_array(RID p_mesh, int p_surface) const { - return PoolVector<uint8_t>(); + + const Mesh *mesh = mesh_owner.getornull(p_mesh); + ERR_FAIL_COND_V(!mesh, PoolVector<uint8_t>()); + ERR_FAIL_INDEX_V(p_surface, mesh->surfaces.size(), PoolVector<uint8_t>()); + + Surface *surface = mesh->surfaces[p_surface]; + + return surface->data; } PoolVector<uint8_t> RasterizerStorageGLES2::mesh_surface_get_index_array(RID p_mesh, int p_surface) const { - return PoolVector<uint8_t>(); + const Mesh *mesh = mesh_owner.getornull(p_mesh); + ERR_FAIL_COND_V(!mesh, PoolVector<uint8_t>()); + ERR_FAIL_INDEX_V(p_surface, mesh->surfaces.size(), PoolVector<uint8_t>()); + + Surface *surface = mesh->surfaces[p_surface]; + + return surface->index_data; } uint32_t RasterizerStorageGLES2::mesh_surface_get_format(RID p_mesh, int p_surface) const { - return 0; + const Mesh *mesh = mesh_owner.getornull(p_mesh); + + ERR_FAIL_COND_V(!mesh, 0); + ERR_FAIL_INDEX_V(p_surface, mesh->surfaces.size(), 0); + + return mesh->surfaces[p_surface]->format; } VS::PrimitiveType RasterizerStorageGLES2::mesh_surface_get_primitive_type(RID p_mesh, int p_surface) const { - return VS::PRIMITIVE_TRIANGLES; + const Mesh *mesh = mesh_owner.getornull(p_mesh); + ERR_FAIL_COND_V(!mesh, VS::PRIMITIVE_MAX); + ERR_FAIL_INDEX_V(p_surface, mesh->surfaces.size(), VS::PRIMITIVE_MAX); + + return mesh->surfaces[p_surface]->primitive; } AABB RasterizerStorageGLES2::mesh_surface_get_aabb(RID p_mesh, int p_surface) const { - return AABB(); + const Mesh *mesh = mesh_owner.getornull(p_mesh); + ERR_FAIL_COND_V(!mesh, AABB()); + ERR_FAIL_INDEX_V(p_surface, mesh->surfaces.size(), AABB()); + + return mesh->surfaces[p_surface]->aabb; } Vector<PoolVector<uint8_t> > RasterizerStorageGLES2::mesh_surface_get_blend_shapes(RID p_mesh, int p_surface) const { + WARN_PRINT("GLES2 mesh_surface_get_blend_shapes is not implemented"); return Vector<PoolVector<uint8_t> >(); } Vector<AABB> RasterizerStorageGLES2::mesh_surface_get_skeleton_aabb(RID p_mesh, int p_surface) const { - return Vector<AABB>(); + const Mesh *mesh = mesh_owner.getornull(p_mesh); + ERR_FAIL_COND_V(!mesh, Vector<AABB>()); + ERR_FAIL_INDEX_V(p_surface, mesh->surfaces.size(), Vector<AABB>()); + + return mesh->surfaces[p_surface]->skeleton_bone_aabb; } void RasterizerStorageGLES2::mesh_remove_surface(RID p_mesh, int p_surface) { + + Mesh *mesh = mesh_owner.getornull(p_mesh); + ERR_FAIL_COND(!mesh); + ERR_FAIL_INDEX(p_surface, mesh->surfaces.size()); + + Surface *surface = mesh->surfaces[p_surface]; + + if (surface->material.is_valid()) { + // TODO _material_remove_geometry(surface->material, mesh->surfaces[p_surface]); + } + + glDeleteBuffers(1, &surface->vertex_id); + if (surface->index_id) { + glDeleteBuffers(1, &surface->index_id); + } + + for (int i = 0; i < surface->blend_shapes.size(); i++) { + glDeleteBuffers(1, &surface->blend_shapes[i].vertex_id); + } + + info.vertex_mem -= surface->total_data_size; + + mesh->instance_material_change_notify(); + + memdelete(surface); + + mesh->surfaces.remove(p_surface); + + mesh->instance_change_notify(); } int RasterizerStorageGLES2::mesh_get_surface_count(RID p_mesh) const { - return 0; + const Mesh *mesh = mesh_owner.getornull(p_mesh); + ERR_FAIL_COND_V(!mesh, 0); + return mesh->surfaces.size(); } void RasterizerStorageGLES2::mesh_set_custom_aabb(RID p_mesh, const AABB &p_aabb) { + Mesh *mesh = mesh_owner.getornull(p_mesh); + ERR_FAIL_COND(!mesh); + + mesh->custom_aabb = p_aabb; } AABB RasterizerStorageGLES2::mesh_get_custom_aabb(RID p_mesh) const { - return AABB(); + const Mesh *mesh = mesh_owner.getornull(p_mesh); + ERR_FAIL_COND_V(!mesh, AABB()); + + return mesh->custom_aabb; } AABB RasterizerStorageGLES2::mesh_get_aabb(RID p_mesh, RID p_skeleton) const { - return AABB(); + Mesh *mesh = mesh_owner.get(p_mesh); + ERR_FAIL_COND_V(!mesh, AABB()); + + if (mesh->custom_aabb != AABB()) + return mesh->custom_aabb; + + // TODO handle skeletons + + AABB aabb; + + if (mesh->surfaces.size() >= 1) { + aabb = mesh->surfaces[0]->aabb; + } + + for (int i = 0; i < mesh->surfaces.size(); i++) { + aabb.merge_with(mesh->surfaces[i]->aabb); + } + + return aabb; } void RasterizerStorageGLES2::mesh_clear(RID p_mesh) { + Mesh *mesh = mesh_owner.getornull(p_mesh); + ERR_FAIL_COND(!mesh); + + while (mesh->surfaces.size()) { + mesh_remove_surface(p_mesh, 0); + } } /* MULTIMESH API */ RID RasterizerStorageGLES2::multimesh_create() { - return RID(); + MultiMesh *multimesh = memnew(MultiMesh); + return multimesh_owner.make_rid(multimesh); } -void RasterizerStorageGLES2::multimesh_allocate(RID p_multimesh, int p_instances, VS::MultimeshTransformFormat p_transform_format, VS::MultimeshColorFormat p_color_format,VS::MultimeshCustomDataFormat p_data) { +void RasterizerStorageGLES2::multimesh_allocate(RID p_multimesh, int p_instances, VS::MultimeshTransformFormat p_transform_format, VS::MultimeshColorFormat p_color_format, VS::MultimeshCustomDataFormat p_data) { + MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh); + ERR_FAIL_COND(!multimesh); + + if (multimesh->size == p_instances && multimesh->transform_format == p_transform_format && multimesh->color_format == p_color_format && multimesh->custom_data_format == p_data) { + return; + } + + multimesh->size = p_instances; + + multimesh->color_format = p_color_format; + multimesh->transform_format = p_transform_format; + multimesh->custom_data_format = p_data; + + if (multimesh->size) { + multimesh->data.resize(0); + } + + if (multimesh->transform_format == VS::MULTIMESH_TRANSFORM_2D) { + multimesh->xform_floats = 8; + } else { + multimesh->xform_floats = 12; + } + + if (multimesh->color_format == VS::MULTIMESH_COLOR_NONE) { + multimesh->color_floats = 0; + } else if (multimesh->color_format == VS::MULTIMESH_COLOR_8BIT) { + multimesh->color_floats = 1; + } else if (multimesh->color_format == VS::MULTIMESH_COLOR_FLOAT) { + multimesh->color_floats = 4; + } + + if (multimesh->custom_data_format == VS::MULTIMESH_CUSTOM_DATA_NONE) { + multimesh->custom_data_floats = 0; + } else if (multimesh->custom_data_format == VS::MULTIMESH_CUSTOM_DATA_8BIT) { + multimesh->custom_data_floats = 1; + } else if (multimesh->custom_data_format == VS::MULTIMESH_CUSTOM_DATA_FLOAT) { + multimesh->custom_data_floats = 4; + } + + int format_floats = multimesh->color_floats + multimesh->xform_floats + multimesh->custom_data_floats; + + multimesh->data.resize(format_floats * p_instances); + + for (int i = 0; i < p_instances * format_floats; i += format_floats) { + int color_from = 0; + int custom_data_from = 0; + + if (multimesh->transform_format == VS::MULTIMESH_TRANSFORM_2D) { + multimesh->data.write[i + 0] = 1.0; + multimesh->data.write[i + 1] = 0.0; + multimesh->data.write[i + 2] = 0.0; + multimesh->data.write[i + 3] = 0.0; + multimesh->data.write[i + 4] = 0.0; + multimesh->data.write[i + 5] = 1.0; + multimesh->data.write[i + 6] = 0.0; + multimesh->data.write[i + 7] = 0.0; + color_from = 8; + custom_data_from = 8; + } else { + multimesh->data.write[i + 0] = 1.0; + multimesh->data.write[i + 1] = 0.0; + multimesh->data.write[i + 2] = 0.0; + multimesh->data.write[i + 3] = 0.0; + multimesh->data.write[i + 4] = 0.0; + multimesh->data.write[i + 5] = 1.0; + multimesh->data.write[i + 6] = 0.0; + multimesh->data.write[i + 7] = 0.0; + multimesh->data.write[i + 8] = 0.0; + multimesh->data.write[i + 9] = 0.0; + multimesh->data.write[i + 10] = 1.0; + multimesh->data.write[i + 11] = 0.0; + color_from = 12; + custom_data_from = 12; + } + + if (multimesh->color_format == VS::MULTIMESH_COLOR_8BIT) { + union { + uint32_t colu; + float colf; + } cu; + + cu.colu = 0xFFFFFFFF; + multimesh->data.write[i + color_from + 0] = cu.colf; + custom_data_from = color_from + 1; + } else if (multimesh->color_format == VS::MULTIMESH_COLOR_FLOAT) { + multimesh->data.write[i + color_from + 0] = 1.0; + multimesh->data.write[i + color_from + 1] = 1.0; + multimesh->data.write[i + color_from + 2] = 1.0; + multimesh->data.write[i + color_from + 3] = 1.0; + custom_data_from = color_from + 4; + } + + if (multimesh->custom_data_format == VS::MULTIMESH_CUSTOM_DATA_8BIT) { + union { + uint32_t colu; + float colf; + } cu; + + cu.colu = 0; + multimesh->data.write[i + custom_data_from + 0] = cu.colf; + } else if (multimesh->custom_data_format == VS::MULTIMESH_CUSTOM_DATA_FLOAT) { + multimesh->data.write[i + custom_data_from + 0] = 0.0; + multimesh->data.write[i + custom_data_from + 1] = 0.0; + multimesh->data.write[i + custom_data_from + 2] = 0.0; + multimesh->data.write[i + custom_data_from + 3] = 0.0; + } + } + + multimesh->dirty_aabb = true; + multimesh->dirty_data = true; + + if (!multimesh->update_list.in_list()) { + multimesh_update_list.add(&multimesh->update_list); + } } int RasterizerStorageGLES2::multimesh_get_instance_count(RID p_multimesh) const { - return 0; + MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh); + ERR_FAIL_COND_V(!multimesh, 0); + + return multimesh->size; } void RasterizerStorageGLES2::multimesh_set_mesh(RID p_multimesh, RID p_mesh) { + MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh); + ERR_FAIL_COND(!multimesh); + + if (multimesh->mesh.is_valid()) { + Mesh *mesh = mesh_owner.getornull(multimesh->mesh); + if (mesh) { + mesh->multimeshes.remove(&multimesh->mesh_list); + } + } + + multimesh->mesh = p_mesh; + + if (multimesh->mesh.is_valid()) { + Mesh *mesh = mesh_owner.getornull(multimesh->mesh); + if (mesh) { + mesh->multimeshes.add(&multimesh->mesh_list); + } + } + + multimesh->dirty_aabb = true; + + if (!multimesh->update_list.in_list()) { + multimesh_update_list.add(&multimesh->update_list); + } } void RasterizerStorageGLES2::multimesh_instance_set_transform(RID p_multimesh, int p_index, const Transform &p_transform) { + MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh); + ERR_FAIL_COND(!multimesh); + ERR_FAIL_INDEX(p_index, multimesh->size); + ERR_FAIL_COND(multimesh->transform_format == VS::MULTIMESH_TRANSFORM_2D); + + int stride = multimesh->color_floats + multimesh->custom_data_floats + multimesh->xform_floats; + + float *dataptr = &multimesh->data.write[stride * p_index]; + + dataptr[0] = p_transform.basis.elements[0][0]; + dataptr[1] = p_transform.basis.elements[0][1]; + dataptr[2] = p_transform.basis.elements[0][2]; + dataptr[3] = p_transform.origin.x; + dataptr[4] = p_transform.basis.elements[1][0]; + dataptr[5] = p_transform.basis.elements[1][1]; + dataptr[6] = p_transform.basis.elements[1][2]; + dataptr[7] = p_transform.origin.y; + dataptr[8] = p_transform.basis.elements[2][0]; + dataptr[9] = p_transform.basis.elements[2][1]; + dataptr[10] = p_transform.basis.elements[2][2]; + dataptr[11] = p_transform.origin.z; + + multimesh->dirty_data = true; + multimesh->dirty_aabb = true; + + if (!multimesh->update_list.in_list()) { + multimesh_update_list.add(&multimesh->update_list); + } } void RasterizerStorageGLES2::multimesh_instance_set_transform_2d(RID p_multimesh, int p_index, const Transform2D &p_transform) { + MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh); + ERR_FAIL_COND(!multimesh); + ERR_FAIL_INDEX(p_index, multimesh->size); + ERR_FAIL_COND(multimesh->transform_format == VS::MULTIMESH_TRANSFORM_3D); + + int stride = multimesh->color_floats + multimesh->xform_floats + multimesh->custom_data_floats; + float *dataptr = &multimesh->data.write[stride * p_index]; + + dataptr[0] = p_transform.elements[0][0]; + dataptr[1] = p_transform.elements[1][0]; + dataptr[2] = 0; + dataptr[3] = p_transform.elements[2][0]; + dataptr[4] = p_transform.elements[0][1]; + dataptr[5] = p_transform.elements[1][1]; + dataptr[6] = 0; + dataptr[7] = p_transform.elements[2][1]; + + multimesh->dirty_data = true; + multimesh->dirty_aabb = true; + + if (!multimesh->update_list.in_list()) { + multimesh_update_list.add(&multimesh->update_list); + } } void RasterizerStorageGLES2::multimesh_instance_set_color(RID p_multimesh, int p_index, const Color &p_color) { + MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh); + ERR_FAIL_COND(!multimesh); + ERR_FAIL_INDEX(p_index, multimesh->size); + ERR_FAIL_COND(multimesh->color_format == VS::MULTIMESH_COLOR_NONE); + + int stride = multimesh->color_floats + multimesh->xform_floats + multimesh->custom_data_floats; + float *dataptr = &multimesh->data.write[stride * p_index + multimesh->xform_floats]; + + if (multimesh->color_format == VS::MULTIMESH_COLOR_8BIT) { + + uint8_t *data8 = (uint8_t *)dataptr; + data8[0] = CLAMP(p_color.r * 255.0, 0, 255); + data8[1] = CLAMP(p_color.g * 255.0, 0, 255); + data8[2] = CLAMP(p_color.b * 255.0, 0, 255); + data8[3] = CLAMP(p_color.a * 255.0, 0, 255); + + } else if (multimesh->color_format == VS::MULTIMESH_COLOR_FLOAT) { + dataptr[0] = p_color.r; + dataptr[1] = p_color.g; + dataptr[2] = p_color.b; + dataptr[3] = p_color.a; + } + + multimesh->dirty_data = true; + multimesh->dirty_aabb = true; + + if (!multimesh->update_list.in_list()) { + multimesh_update_list.add(&multimesh->update_list); + } } -void RasterizerStorageGLES2::multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_color) { +void RasterizerStorageGLES2::multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_custom_data) { + MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh); + ERR_FAIL_COND(!multimesh); + ERR_FAIL_INDEX(p_index, multimesh->size); + ERR_FAIL_COND(multimesh->custom_data_format == VS::MULTIMESH_CUSTOM_DATA_NONE); + + int stride = multimesh->color_floats + multimesh->xform_floats + multimesh->custom_data_floats; + float *dataptr = &multimesh->data.write[stride * p_index + multimesh->xform_floats + multimesh->color_floats]; + + if (multimesh->custom_data_format == VS::MULTIMESH_CUSTOM_DATA_8BIT) { + + uint8_t *data8 = (uint8_t *)dataptr; + data8[0] = CLAMP(p_custom_data.r * 255.0, 0, 255); + data8[1] = CLAMP(p_custom_data.g * 255.0, 0, 255); + data8[2] = CLAMP(p_custom_data.b * 255.0, 0, 255); + data8[3] = CLAMP(p_custom_data.a * 255.0, 0, 255); + + } else if (multimesh->custom_data_format == VS::MULTIMESH_CUSTOM_DATA_FLOAT) { + dataptr[0] = p_custom_data.r; + dataptr[1] = p_custom_data.g; + dataptr[2] = p_custom_data.b; + dataptr[3] = p_custom_data.a; + } + + multimesh->dirty_data = true; + multimesh->dirty_aabb = true; + + if (!multimesh->update_list.in_list()) { + multimesh_update_list.add(&multimesh->update_list); + } } RID RasterizerStorageGLES2::multimesh_get_mesh(RID p_multimesh) const { - return RID(); + MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh); + ERR_FAIL_COND_V(!multimesh, RID()); + + return multimesh->mesh; } Transform RasterizerStorageGLES2::multimesh_instance_get_transform(RID p_multimesh, int p_index) const { - return Transform(); + MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh); + ERR_FAIL_COND_V(!multimesh, Transform()); + ERR_FAIL_INDEX_V(p_index, multimesh->size, Transform()); + ERR_FAIL_COND_V(multimesh->transform_format == VS::MULTIMESH_TRANSFORM_2D, Transform()); + + int stride = multimesh->color_floats + multimesh->xform_floats + multimesh->custom_data_floats; + float *dataptr = &multimesh->data.write[stride * p_index]; + + Transform xform; + + xform.basis.elements[0][0] = dataptr[0]; + xform.basis.elements[0][1] = dataptr[1]; + xform.basis.elements[0][2] = dataptr[2]; + xform.origin.x = dataptr[3]; + xform.basis.elements[1][0] = dataptr[4]; + xform.basis.elements[1][1] = dataptr[5]; + xform.basis.elements[1][2] = dataptr[6]; + xform.origin.y = dataptr[7]; + xform.basis.elements[2][0] = dataptr[8]; + xform.basis.elements[2][1] = dataptr[9]; + xform.basis.elements[2][2] = dataptr[10]; + xform.origin.z = dataptr[11]; + + return xform; } Transform2D RasterizerStorageGLES2::multimesh_instance_get_transform_2d(RID p_multimesh, int p_index) const { - return Transform2D(); + MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh); + ERR_FAIL_COND_V(!multimesh, Transform2D()); + ERR_FAIL_INDEX_V(p_index, multimesh->size, Transform2D()); + ERR_FAIL_COND_V(multimesh->transform_format == VS::MULTIMESH_TRANSFORM_3D, Transform2D()); + + int stride = multimesh->color_floats + multimesh->xform_floats + multimesh->custom_data_floats; + float *dataptr = &multimesh->data.write[stride * p_index]; + + Transform2D xform; + + xform.elements[0][0] = dataptr[0]; + xform.elements[1][0] = dataptr[1]; + xform.elements[2][0] = dataptr[3]; + xform.elements[0][1] = dataptr[4]; + xform.elements[1][1] = dataptr[5]; + xform.elements[2][1] = dataptr[7]; + + return xform; } Color RasterizerStorageGLES2::multimesh_instance_get_color(RID p_multimesh, int p_index) const { + MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh); + ERR_FAIL_COND_V(!multimesh, Color()); + ERR_FAIL_INDEX_V(p_index, multimesh->size, Color()); + ERR_FAIL_COND_V(multimesh->color_format == VS::MULTIMESH_COLOR_NONE, Color()); + + int stride = multimesh->color_floats + multimesh->xform_floats + multimesh->custom_data_floats; + float *dataptr = &multimesh->data.write[stride * p_index + multimesh->xform_floats]; + + if (multimesh->color_format == VS::MULTIMESH_COLOR_8BIT) { + union { + uint32_t colu; + float colf; + } cu; + + cu.colf = dataptr[0]; + + return Color::hex(BSWAP32(cu.colu)); + + } else if (multimesh->color_format == VS::MULTIMESH_COLOR_FLOAT) { + Color c; + c.r = dataptr[0]; + c.g = dataptr[1]; + c.b = dataptr[2]; + c.a = dataptr[3]; + + return c; + } + return Color(); } Color RasterizerStorageGLES2::multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const { + MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh); + ERR_FAIL_COND_V(!multimesh, Color()); + ERR_FAIL_INDEX_V(p_index, multimesh->size, Color()); + ERR_FAIL_COND_V(multimesh->custom_data_format == VS::MULTIMESH_CUSTOM_DATA_NONE, Color()); + + int stride = multimesh->color_floats + multimesh->xform_floats + multimesh->custom_data_floats; + float *dataptr = &multimesh->data.write[stride * p_index + multimesh->xform_floats + multimesh->color_floats]; + + if (multimesh->custom_data_format == VS::MULTIMESH_CUSTOM_DATA_8BIT) { + union { + uint32_t colu; + float colf; + } cu; + + cu.colf = dataptr[0]; + + return Color::hex(BSWAP32(cu.colu)); + + } else if (multimesh->custom_data_format == VS::MULTIMESH_CUSTOM_DATA_FLOAT) { + Color c; + c.r = dataptr[0]; + c.g = dataptr[1]; + c.b = dataptr[2]; + c.a = dataptr[3]; + + return c; + } + return Color(); } void RasterizerStorageGLES2::multimesh_set_as_bulk_array(RID p_multimesh, const PoolVector<float> &p_array) { + MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh); + ERR_FAIL_COND(!multimesh); + int dsize = multimesh->data.size(); + ERR_FAIL_COND(dsize != p_array.size()); + + PoolVector<float>::Read r = p_array.read(); + copymem(multimesh->data.ptrw(), r.ptr(), dsize * sizeof(float)); + + multimesh->dirty_data = true; + multimesh->dirty_aabb = true; + + if (!multimesh->update_list.in_list()) { + multimesh_update_list.add(&multimesh->update_list); + } } void RasterizerStorageGLES2::multimesh_set_visible_instances(RID p_multimesh, int p_visible) { + MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh); + ERR_FAIL_COND(!multimesh); + + multimesh->visible_instances = p_visible; } int RasterizerStorageGLES2::multimesh_get_visible_instances(RID p_multimesh) const { - return 0; + MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh); + ERR_FAIL_COND_V(!multimesh, -1); + + return multimesh->visible_instances; } AABB RasterizerStorageGLES2::multimesh_get_aabb(RID p_multimesh) const { + MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh); + ERR_FAIL_COND_V(!multimesh, AABB()); - return AABB(); + const_cast<RasterizerStorageGLES2 *>(this)->update_dirty_multimeshes(); + + return multimesh->aabb; } void RasterizerStorageGLES2::update_dirty_multimeshes() { + + while (multimesh_update_list.first()) { + + MultiMesh *multimesh = multimesh_update_list.first()->self(); + + if (multimesh->size && multimesh->dirty_aabb) { + + AABB mesh_aabb; + + if (multimesh->mesh.is_valid()) { + mesh_aabb = mesh_get_aabb(multimesh->mesh, RID()); + } else { + mesh_aabb.size += Vector3(0.001, 0.001, 0.001); + } + + int stride = multimesh->color_floats + multimesh->xform_floats + multimesh->custom_data_floats; + int count = multimesh->data.size(); + float *data = multimesh->data.ptrw(); + + AABB aabb; + + if (multimesh->transform_format == VS::MULTIMESH_TRANSFORM_2D) { + + for (int i = 0; i < count; i += stride) { + + float *dataptr = &data[i]; + + Transform xform; + xform.basis[0][0] = dataptr[0]; + xform.basis[0][1] = dataptr[1]; + xform.origin[0] = dataptr[3]; + xform.basis[1][0] = dataptr[4]; + xform.basis[1][1] = dataptr[5]; + xform.origin[1] = dataptr[7]; + + AABB laabb = xform.xform(mesh_aabb); + + if (i == 0) { + aabb = laabb; + } else { + aabb.merge_with(laabb); + } + } + + } else { + + for (int i = 0; i < count; i += stride) { + + float *dataptr = &data[i]; + + Transform xform; + xform.basis.elements[0][0] = dataptr[0]; + xform.basis.elements[0][1] = dataptr[1]; + xform.basis.elements[0][2] = dataptr[2]; + xform.origin.x = dataptr[3]; + xform.basis.elements[1][0] = dataptr[4]; + xform.basis.elements[1][1] = dataptr[5]; + xform.basis.elements[1][2] = dataptr[6]; + xform.origin.y = dataptr[7]; + xform.basis.elements[2][0] = dataptr[8]; + xform.basis.elements[2][1] = dataptr[9]; + xform.basis.elements[2][2] = dataptr[10]; + xform.origin.z = dataptr[11]; + + AABB laabb = xform.xform(mesh_aabb); + + if (i == 0) { + aabb = laabb; + } else { + aabb.merge_with(laabb); + } + } + } + + multimesh->aabb = aabb; + } + + multimesh->dirty_aabb = false; + multimesh->dirty_data = false; + + multimesh->instance_change_notify(); + + multimesh_update_list.remove(multimesh_update_list.first()); + } } /* IMMEDIATE API */ RID RasterizerStorageGLES2::immediate_create() { - return RID(); + Immediate *im = memnew(Immediate); + return immediate_owner.make_rid(im); } -void RasterizerStorageGLES2::immediate_begin(RID p_immediate, VS::PrimitiveType p_rimitive, RID p_texture) { +void RasterizerStorageGLES2::immediate_begin(RID p_immediate, VS::PrimitiveType p_primitive, RID p_texture) { + Immediate *im = immediate_owner.get(p_immediate); + ERR_FAIL_COND(!im); + ERR_FAIL_COND(im->building); + + Immediate::Chunk ic; + ic.texture = p_texture; + ic.primitive = p_primitive; + im->chunks.push_back(ic); + im->mask = 0; + im->building = true; } void RasterizerStorageGLES2::immediate_vertex(RID p_immediate, const Vector3 &p_vertex) { + Immediate *im = immediate_owner.get(p_immediate); + ERR_FAIL_COND(!im); + ERR_FAIL_COND(!im->building); + + Immediate::Chunk *c = &im->chunks.back()->get(); + + if (c->vertices.empty() && im->chunks.size() == 1) { + im->aabb.position = p_vertex; + im->aabb.size = Vector3(); + } else { + im->aabb.expand_to(p_vertex); + } + + if (im->mask & VS::ARRAY_FORMAT_NORMAL) + c->normals.push_back(chunk_normal); + if (im->mask & VS::ARRAY_FORMAT_TANGENT) + c->tangents.push_back(chunk_tangent); + if (im->mask & VS::ARRAY_FORMAT_COLOR) + c->colors.push_back(chunk_color); + if (im->mask & VS::ARRAY_FORMAT_TEX_UV) + c->uvs.push_back(chunk_uv); + if (im->mask & VS::ARRAY_FORMAT_TEX_UV2) + c->uv2s.push_back(chunk_uv2); + im->mask |= VS::ARRAY_FORMAT_VERTEX; + c->vertices.push_back(p_vertex); } void RasterizerStorageGLES2::immediate_normal(RID p_immediate, const Vector3 &p_normal) { + Immediate *im = immediate_owner.get(p_immediate); + ERR_FAIL_COND(!im); + ERR_FAIL_COND(!im->building); + + im->mask |= VS::ARRAY_FORMAT_NORMAL; + chunk_normal = p_normal; } void RasterizerStorageGLES2::immediate_tangent(RID p_immediate, const Plane &p_tangent) { + Immediate *im = immediate_owner.get(p_immediate); + ERR_FAIL_COND(!im); + ERR_FAIL_COND(!im->building); + + im->mask |= VS::ARRAY_FORMAT_TANGENT; + chunk_tangent = p_tangent; } void RasterizerStorageGLES2::immediate_color(RID p_immediate, const Color &p_color) { + Immediate *im = immediate_owner.get(p_immediate); + ERR_FAIL_COND(!im); + ERR_FAIL_COND(!im->building); + + im->mask |= VS::ARRAY_FORMAT_COLOR; + chunk_color = p_color; } void RasterizerStorageGLES2::immediate_uv(RID p_immediate, const Vector2 &tex_uv) { + Immediate *im = immediate_owner.get(p_immediate); + ERR_FAIL_COND(!im); + ERR_FAIL_COND(!im->building); + + im->mask |= VS::ARRAY_FORMAT_TEX_UV; + chunk_uv = tex_uv; } void RasterizerStorageGLES2::immediate_uv2(RID p_immediate, const Vector2 &tex_uv) { + Immediate *im = immediate_owner.get(p_immediate); + ERR_FAIL_COND(!im); + ERR_FAIL_COND(!im->building); + + im->mask |= VS::ARRAY_FORMAT_TEX_UV2; + chunk_uv2 = tex_uv; } void RasterizerStorageGLES2::immediate_end(RID p_immediate) { + Immediate *im = immediate_owner.get(p_immediate); + ERR_FAIL_COND(!im); + ERR_FAIL_COND(!im->building); + + im->building = false; + im->instance_change_notify(); } void RasterizerStorageGLES2::immediate_clear(RID p_immediate) { + Immediate *im = immediate_owner.get(p_immediate); + ERR_FAIL_COND(!im); + ERR_FAIL_COND(im->building); + + im->chunks.clear(); + im->instance_change_notify(); } AABB RasterizerStorageGLES2::immediate_get_aabb(RID p_immediate) const { - return AABB(); + Immediate *im = immediate_owner.get(p_immediate); + ERR_FAIL_COND_V(!im, AABB()); + return im->aabb; } void RasterizerStorageGLES2::immediate_set_material(RID p_immediate, RID p_material) { + Immediate *im = immediate_owner.get(p_immediate); + ERR_FAIL_COND(!im); + + im->material = p_material; + im->instance_material_change_notify(); } RID RasterizerStorageGLES2::immediate_get_material(RID p_immediate) const { - return RID(); + const Immediate *im = immediate_owner.get(p_immediate); + ERR_FAIL_COND_V(!im, RID()); + return im->material; } /* SKELETON API */ RID RasterizerStorageGLES2::skeleton_create() { - return RID(); + + Skeleton *skeleton = memnew(Skeleton); + + return skeleton_owner.make_rid(skeleton); } void RasterizerStorageGLES2::skeleton_allocate(RID p_skeleton, int p_bones, bool p_2d_skeleton) { + + Skeleton *skeleton = skeleton_owner.getornull(p_skeleton); + ERR_FAIL_COND(!skeleton); + ERR_FAIL_COND(p_bones < 0); + + if (skeleton->size == p_bones && skeleton->use_2d == p_2d_skeleton) { + return; + } + + skeleton->size = p_bones; + skeleton->use_2d = p_2d_skeleton; + + // TODO use float texture for vertex shader + if (config.float_texture_supported) { + glGenTextures(1, &skeleton->tex_id); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, skeleton->tex_id); + +#ifdef GLES_OVER_GL + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, p_bones * 3, 1, 0, GL_RGBA, GL_FLOAT, NULL); +#else + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, p_bones * 3, 1, 0, GL_RGBA, GL_FLOAT, NULL); +#endif + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glBindTexture(GL_TEXTURE_2D, 0); + } + if (skeleton->use_2d) { + skeleton->bone_data.resize(p_bones * 4 * 2); + } else { + skeleton->bone_data.resize(p_bones * 4 * 3); + } } int RasterizerStorageGLES2::skeleton_get_bone_count(RID p_skeleton) const { - return 0; + Skeleton *skeleton = skeleton_owner.getornull(p_skeleton); + ERR_FAIL_COND_V(!skeleton, 0); + + return skeleton->size; } void RasterizerStorageGLES2::skeleton_bone_set_transform(RID p_skeleton, int p_bone, const Transform &p_transform) { + Skeleton *skeleton = skeleton_owner.getornull(p_skeleton); + ERR_FAIL_COND(!skeleton); + + ERR_FAIL_INDEX(p_bone, skeleton->size); + ERR_FAIL_COND(skeleton->use_2d); + + float *bone_data = skeleton->bone_data.ptrw(); + + int base_offset = p_bone * 4 * 3; + + bone_data[base_offset + 0] = p_transform.basis[0].x; + bone_data[base_offset + 1] = p_transform.basis[0].y; + bone_data[base_offset + 2] = p_transform.basis[0].z; + bone_data[base_offset + 3] = p_transform.origin.x; + + bone_data[base_offset + 4] = p_transform.basis[1].x; + bone_data[base_offset + 5] = p_transform.basis[1].y; + bone_data[base_offset + 6] = p_transform.basis[1].z; + bone_data[base_offset + 7] = p_transform.origin.y; + + bone_data[base_offset + 8] = p_transform.basis[2].x; + bone_data[base_offset + 9] = p_transform.basis[2].y; + bone_data[base_offset + 10] = p_transform.basis[2].z; + bone_data[base_offset + 11] = p_transform.origin.z; + + if (!skeleton->update_list.in_list()) { + skeleton_update_list.add(&skeleton->update_list); + } } Transform RasterizerStorageGLES2::skeleton_bone_get_transform(RID p_skeleton, int p_bone) const { - return Transform(); + Skeleton *skeleton = skeleton_owner.getornull(p_skeleton); + ERR_FAIL_COND_V(!skeleton, Transform()); + + ERR_FAIL_INDEX_V(p_bone, skeleton->size, Transform()); + ERR_FAIL_COND_V(skeleton->use_2d, Transform()); + + const float *bone_data = skeleton->bone_data.ptr(); + + Transform ret; + + int base_offset = p_bone * 4 * 3; + + ret.basis[0].x = bone_data[base_offset + 0]; + ret.basis[0].y = bone_data[base_offset + 1]; + ret.basis[0].z = bone_data[base_offset + 2]; + ret.origin.x = bone_data[base_offset + 3]; + + ret.basis[1].x = bone_data[base_offset + 4]; + ret.basis[1].y = bone_data[base_offset + 5]; + ret.basis[1].z = bone_data[base_offset + 6]; + ret.origin.y = bone_data[base_offset + 7]; + + ret.basis[2].x = bone_data[base_offset + 8]; + ret.basis[2].y = bone_data[base_offset + 9]; + ret.basis[2].z = bone_data[base_offset + 10]; + ret.origin.z = bone_data[base_offset + 11]; + + return ret; } void RasterizerStorageGLES2::skeleton_bone_set_transform_2d(RID p_skeleton, int p_bone, const Transform2D &p_transform) { + Skeleton *skeleton = skeleton_owner.getornull(p_skeleton); + ERR_FAIL_COND(!skeleton); + + ERR_FAIL_INDEX(p_bone, skeleton->size); + ERR_FAIL_COND(!skeleton->use_2d); + + float *bone_data = skeleton->bone_data.ptrw(); + + int base_offset = p_bone * 4 * 2; + + bone_data[base_offset + 0] = p_transform[0][0]; + bone_data[base_offset + 1] = p_transform[1][0]; + bone_data[base_offset + 2] = 0; + bone_data[base_offset + 3] = p_transform[2][0]; + bone_data[base_offset + 4] = p_transform[0][1]; + bone_data[base_offset + 5] = p_transform[1][1]; + bone_data[base_offset + 6] = 0; + bone_data[base_offset + 7] = p_transform[2][1]; + + if (!skeleton->update_list.in_list()) { + skeleton_update_list.add(&skeleton->update_list); + } } Transform2D RasterizerStorageGLES2::skeleton_bone_get_transform_2d(RID p_skeleton, int p_bone) const { - return Transform2D(); + Skeleton *skeleton = skeleton_owner.getornull(p_skeleton); + ERR_FAIL_COND_V(!skeleton, Transform2D()); + + ERR_FAIL_INDEX_V(p_bone, skeleton->size, Transform2D()); + ERR_FAIL_COND_V(!skeleton->use_2d, Transform2D()); + + const float *bone_data = skeleton->bone_data.ptr(); + + Transform2D ret; + + int base_offset = p_bone * 4 * 2; + + ret[0][0] = bone_data[base_offset + 0]; + ret[1][0] = bone_data[base_offset + 1]; + ret[2][0] = bone_data[base_offset + 3]; + ret[0][1] = bone_data[base_offset + 4]; + ret[1][1] = bone_data[base_offset + 5]; + ret[2][1] = bone_data[base_offset + 7]; + + return ret; } void RasterizerStorageGLES2::skeleton_set_base_transform_2d(RID p_skeleton, const Transform2D &p_base_transform) { } +void RasterizerStorageGLES2::_update_skeleton_transform_buffer(const PoolVector<float> &p_data, size_t p_size) { + + glBindBuffer(GL_ARRAY_BUFFER, resources.skeleton_transform_buffer); + + if (p_size > resources.skeleton_transform_buffer_size) { + // new requested buffer is bigger, so resizing the GPU buffer + + resources.skeleton_transform_buffer_size = p_size; + + glBufferData(GL_ARRAY_BUFFER, p_size * sizeof(float), p_data.read().ptr(), GL_DYNAMIC_DRAW); + } else { + glBufferSubData(GL_ARRAY_BUFFER, 0, p_size * sizeof(float), p_data.read().ptr()); + } + + glBindBuffer(GL_ARRAY_BUFFER, 0); +} + void RasterizerStorageGLES2::update_dirty_skeletons() { + + if (!config.float_texture_supported) + return; + + glActiveTexture(GL_TEXTURE0); + + while (skeleton_update_list.first()) { + Skeleton *skeleton = skeleton_update_list.first()->self(); + + if (skeleton->size) { + glBindTexture(GL_TEXTURE_2D, skeleton->tex_id); + + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, skeleton->size * 3, 1, GL_RGBA, GL_FLOAT, skeleton->bone_data.ptr()); + } + + for (Set<RasterizerScene::InstanceBase *>::Element *E = skeleton->instances.front(); E; E = E->next()) { + E->get()->base_changed(); + } + + skeleton_update_list.remove(skeleton_update_list.first()); + } } /* Light API */ RID RasterizerStorageGLES2::light_create(VS::LightType p_type) { - return RID(); + + Light *light = memnew(Light); + + light->type = p_type; + + light->param[VS::LIGHT_PARAM_ENERGY] = 1.0; + light->param[VS::LIGHT_PARAM_INDIRECT_ENERGY] = 1.0; + light->param[VS::LIGHT_PARAM_SPECULAR] = 0.5; + light->param[VS::LIGHT_PARAM_RANGE] = 1.0; + light->param[VS::LIGHT_PARAM_SPOT_ANGLE] = 45; + light->param[VS::LIGHT_PARAM_CONTACT_SHADOW_SIZE] = 45; + light->param[VS::LIGHT_PARAM_SHADOW_MAX_DISTANCE] = 0; + light->param[VS::LIGHT_PARAM_SHADOW_SPLIT_1_OFFSET] = 0.1; + light->param[VS::LIGHT_PARAM_SHADOW_SPLIT_2_OFFSET] = 0.3; + light->param[VS::LIGHT_PARAM_SHADOW_SPLIT_3_OFFSET] = 0.6; + light->param[VS::LIGHT_PARAM_SHADOW_NORMAL_BIAS] = 0.1; + light->param[VS::LIGHT_PARAM_SHADOW_BIAS_SPLIT_SCALE] = 0.1; + + light->color = Color(1, 1, 1, 1); + light->shadow = false; + light->negative = false; + light->cull_mask = 0xFFFFFFFF; + light->directional_shadow_mode = VS::LIGHT_DIRECTIONAL_SHADOW_ORTHOGONAL; + light->omni_shadow_mode = VS::LIGHT_OMNI_SHADOW_DUAL_PARABOLOID; + light->omni_shadow_detail = VS::LIGHT_OMNI_SHADOW_DETAIL_VERTICAL; + light->directional_blend_splits = false; + light->directional_range_mode = VS::LIGHT_DIRECTIONAL_SHADOW_DEPTH_RANGE_STABLE; + light->reverse_cull = false; + light->version = 0; + + return light_owner.make_rid(light); } void RasterizerStorageGLES2::light_set_color(RID p_light, const Color &p_color) { + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND(!light); + + light->color = p_color; } void RasterizerStorageGLES2::light_set_param(RID p_light, VS::LightParam p_param, float p_value) { + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND(!light); + ERR_FAIL_INDEX(p_param, VS::LIGHT_PARAM_MAX); + + switch (p_param) { + case VS::LIGHT_PARAM_RANGE: + case VS::LIGHT_PARAM_SPOT_ANGLE: + case VS::LIGHT_PARAM_SHADOW_MAX_DISTANCE: + case VS::LIGHT_PARAM_SHADOW_SPLIT_1_OFFSET: + case VS::LIGHT_PARAM_SHADOW_SPLIT_2_OFFSET: + case VS::LIGHT_PARAM_SHADOW_SPLIT_3_OFFSET: + case VS::LIGHT_PARAM_SHADOW_NORMAL_BIAS: + case VS::LIGHT_PARAM_SHADOW_BIAS: { + light->version++; + light->instance_change_notify(); + } break; + } + + light->param[p_param] = p_value; } void RasterizerStorageGLES2::light_set_shadow(RID p_light, bool p_enabled) { + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND(!light); + + light->shadow = p_enabled; + + light->version++; + light->instance_change_notify(); } void RasterizerStorageGLES2::light_set_shadow_color(RID p_light, const Color &p_color) { + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND(!light); + + light->shadow_color = p_color; } void RasterizerStorageGLES2::light_set_projector(RID p_light, RID p_texture) { + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND(!light); + + light->projector = p_texture; } void RasterizerStorageGLES2::light_set_negative(RID p_light, bool p_enable) { + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND(!light); + + light->negative = p_enable; } void RasterizerStorageGLES2::light_set_cull_mask(RID p_light, uint32_t p_mask) { + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND(!light); + + light->cull_mask = p_mask; + + light->version++; + light->instance_change_notify(); } void RasterizerStorageGLES2::light_set_reverse_cull_face_mode(RID p_light, bool p_enabled) { + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND(!light); + + light->reverse_cull = p_enabled; } void RasterizerStorageGLES2::light_omni_set_shadow_mode(RID p_light, VS::LightOmniShadowMode p_mode) { + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND(!light); + + light->omni_shadow_mode = p_mode; + + light->version++; + light->instance_change_notify(); } VS::LightOmniShadowMode RasterizerStorageGLES2::light_omni_get_shadow_mode(RID p_light) { - return VS::LIGHT_OMNI_SHADOW_DUAL_PARABOLOID; + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND_V(!light, VS::LIGHT_OMNI_SHADOW_CUBE); + + return light->omni_shadow_mode; } void RasterizerStorageGLES2::light_omni_set_shadow_detail(RID p_light, VS::LightOmniShadowDetail p_detail) { + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND(!light); + + light->omni_shadow_detail = p_detail; + + light->version++; + light->instance_change_notify(); } void RasterizerStorageGLES2::light_directional_set_shadow_mode(RID p_light, VS::LightDirectionalShadowMode p_mode) { + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND(!light); + + light->directional_shadow_mode = p_mode; + + light->version++; + light->instance_change_notify(); } void RasterizerStorageGLES2::light_directional_set_blend_splits(RID p_light, bool p_enable) { + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND(!light); + + light->directional_blend_splits = p_enable; + + light->version++; + light->instance_change_notify(); } bool RasterizerStorageGLES2::light_directional_get_blend_splits(RID p_light) const { - return false; + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND_V(!light, false); + return light->directional_blend_splits; } VS::LightDirectionalShadowMode RasterizerStorageGLES2::light_directional_get_shadow_mode(RID p_light) { - return VS::LIGHT_DIRECTIONAL_SHADOW_ORTHOGONAL; + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND_V(!light, VS::LIGHT_DIRECTIONAL_SHADOW_ORTHOGONAL); + return light->directional_shadow_mode; } void RasterizerStorageGLES2::light_directional_set_shadow_depth_range_mode(RID p_light, VS::LightDirectionalShadowDepthRangeMode p_range_mode) { + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND(!light); + + light->directional_range_mode = p_range_mode; } VS::LightDirectionalShadowDepthRangeMode RasterizerStorageGLES2::light_directional_get_shadow_depth_range_mode(RID p_light) const { - return VS::LIGHT_DIRECTIONAL_SHADOW_DEPTH_RANGE_STABLE; + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND_V(!light, VS::LIGHT_DIRECTIONAL_SHADOW_DEPTH_RANGE_STABLE); + + return light->directional_range_mode; } VS::LightType RasterizerStorageGLES2::light_get_type(RID p_light) const { - return VS::LIGHT_DIRECTIONAL; + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND_V(!light, VS::LIGHT_DIRECTIONAL); + + return light->type; } float RasterizerStorageGLES2::light_get_param(RID p_light, VS::LightParam p_param) { + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND_V(!light, 0.0); + ERR_FAIL_INDEX_V(p_param, VS::LIGHT_PARAM_MAX, 0.0); - return VS::LIGHT_DIRECTIONAL; + return light->param[p_param]; } Color RasterizerStorageGLES2::light_get_color(RID p_light) { - return Color(); + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND_V(!light, Color()); + + return light->color; } bool RasterizerStorageGLES2::light_has_shadow(RID p_light) const { + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND_V(!light, false); - return VS::LIGHT_DIRECTIONAL; + return light->shadow; } uint64_t RasterizerStorageGLES2::light_get_version(RID p_light) const { - return 0; + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND_V(!light, 0); + + return light->version; } AABB RasterizerStorageGLES2::light_get_aabb(RID p_light) const { + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND_V(!light, AABB()); + + switch (light->type) { + + case VS::LIGHT_SPOT: { + float len = light->param[VS::LIGHT_PARAM_RANGE]; + float size = Math::tan(Math::deg2rad(light->param[VS::LIGHT_PARAM_SPOT_ANGLE])) * len; + return AABB(Vector3(-size, -size, -len), Vector3(size * 2, size * 2, len)); + } break; + + case VS::LIGHT_OMNI: { + float r = light->param[VS::LIGHT_PARAM_RANGE]; + return AABB(-Vector3(r, r, r), Vector3(r, r, r) * 2); + } break; + + case VS::LIGHT_DIRECTIONAL: { + return AABB(); + } break; + } + + ERR_FAIL_V(AABB()); return AABB(); } @@ -1788,6 +3667,9 @@ void RasterizerStorageGLES2::_render_target_allocate(RenderTarget *rt) { texture_set_flags(rt->texture, texture->flags); + glClearColor(0, 0, 0, 0); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + // copy texscreen buffers { int w = rt->width; @@ -1861,6 +3743,7 @@ RID RasterizerStorageGLES2::render_target_create() { t->data_size = 0; t->total_data_size = 0; t->ignore_mipmaps = false; + t->compressed = false; t->mipmaps = 1; t->active = true; t->tex_id = 0; @@ -1957,14 +3840,205 @@ void RasterizerStorageGLES2::canvas_light_occluder_set_polylines(RID p_occluder, } VS::InstanceType RasterizerStorageGLES2::get_base_type(RID p_rid) const { - return VS::INSTANCE_NONE; + + if (mesh_owner.owns(p_rid)) { + return VS::INSTANCE_MESH; + } else if (light_owner.owns(p_rid)) { + return VS::INSTANCE_LIGHT; + } else if (multimesh_owner.owns(p_rid)) { + return VS::INSTANCE_MULTIMESH; + } else if (immediate_owner.owns(p_rid)) { + return VS::INSTANCE_IMMEDIATE; + } else { + return VS::INSTANCE_NONE; + } } bool RasterizerStorageGLES2::free(RID p_rid) { - return false; + + if (render_target_owner.owns(p_rid)) { + + RenderTarget *rt = render_target_owner.getornull(p_rid); + _render_target_clear(rt); + + Texture *t = texture_owner.get(rt->texture); + texture_owner.free(rt->texture); + memdelete(t); + render_target_owner.free(p_rid); + memdelete(rt); + + return true; + } else if (texture_owner.owns(p_rid)) { + + Texture *t = texture_owner.get(p_rid); + // can't free a render target texture + ERR_FAIL_COND_V(t->render_target, true); + + info.texture_mem -= t->total_data_size; + texture_owner.free(p_rid); + memdelete(t); + + return true; + } else if (sky_owner.owns(p_rid)) { + + Sky *sky = sky_owner.get(p_rid); + sky_set_texture(p_rid, RID(), 256); + sky_owner.free(p_rid); + memdelete(sky); + + return true; + } else if (shader_owner.owns(p_rid)) { + + Shader *shader = shader_owner.get(p_rid); + + if (shader->shader) { + shader->shader->free_custom_shader(shader->custom_code_id); + } + + if (shader->dirty_list.in_list()) { + _shader_dirty_list.remove(&shader->dirty_list); + } + + while (shader->materials.first()) { + Material *m = shader->materials.first()->self(); + + m->shader = NULL; + _material_make_dirty(m); + + shader->materials.remove(shader->materials.first()); + } + + shader_owner.free(p_rid); + memdelete(shader); + + return true; + } else if (material_owner.owns(p_rid)) { + + Material *m = material_owner.get(p_rid); + + if (m->shader) { + m->shader->materials.remove(&m->list); + } + + for (Map<Geometry *, int>::Element *E = m->geometry_owners.front(); E; E = E->next()) { + Geometry *g = E->key(); + g->material = RID(); + } + + for (Map<RasterizerScene::InstanceBase *, int>::Element *E = m->instance_owners.front(); E; E = E->next()) { + + RasterizerScene::InstanceBase *ins = E->key(); + + if (ins->material_override == p_rid) { + ins->material_override = RID(); + } + + for (int i = 0; i < ins->materials.size(); i++) { + if (ins->materials[i] == p_rid) { + ins->materials.write[i] = RID(); + } + } + } + + material_owner.free(p_rid); + memdelete(m); + + return true; + } else if (skeleton_owner.owns(p_rid)) { + + Skeleton *s = skeleton_owner.get(p_rid); + + if (s->update_list.in_list()) { + skeleton_update_list.remove(&s->update_list); + } + + for (Set<RasterizerScene::InstanceBase *>::Element *E = s->instances.front(); E; E = E->next()) { + E->get()->skeleton = RID(); + } + + skeleton_allocate(p_rid, 0, false); + + if (s->tex_id) { + glDeleteTextures(1, &s->tex_id); + } + + skeleton_owner.free(p_rid); + memdelete(s); + + return true; + } else if (mesh_owner.owns(p_rid)) { + + Mesh *mesh = mesh_owner.get(p_rid); + + mesh->instance_remove_deps(); + mesh_clear(p_rid); + + while (mesh->multimeshes.first()) { + MultiMesh *multimesh = mesh->multimeshes.first()->self(); + multimesh->mesh = RID(); + multimesh->dirty_aabb = true; + + mesh->multimeshes.remove(mesh->multimeshes.first()); + + if (!multimesh->update_list.in_list()) { + multimesh_update_list.add(&multimesh->update_list); + } + } + + mesh_owner.free(p_rid); + memdelete(mesh); + + return true; + } else if (multimesh_owner.owns(p_rid)) { + + MultiMesh *multimesh = multimesh_owner.get(p_rid); + multimesh->instance_remove_deps(); + + if (multimesh->mesh.is_valid()) { + Mesh *mesh = mesh_owner.getornull(multimesh->mesh); + if (mesh) { + mesh->multimeshes.remove(&multimesh->mesh_list); + } + } + + multimesh_allocate(p_rid, 0, VS::MULTIMESH_TRANSFORM_3D, VS::MULTIMESH_COLOR_NONE); + + update_dirty_multimeshes(); + + multimesh_owner.free(p_rid); + memdelete(multimesh); + + return true; + } else if (immediate_owner.owns(p_rid)) { + Immediate *im = immediate_owner.get(p_rid); + im->instance_remove_deps(); + + immediate_owner.free(p_rid); + memdelete(im); + + return true; + } else if (light_owner.owns(p_rid)) { + + Light *light = light_owner.get(p_rid); + light->instance_remove_deps(); + + light_owner.free(p_rid); + memdelete(light); + + return true; + } else { + return false; + } } bool RasterizerStorageGLES2::has_os_feature(const String &p_feature) const { + + if (p_feature == "s3tc") + return config.s3tc_supported; + + if (p_feature == "etc") + return config.etc1_supported; + return false; } @@ -1992,24 +4066,63 @@ void RasterizerStorageGLES2::initialize() { RasterizerStorageGLES2::system_fbo = 0; { - const char *gl_extensions = (const char *)glGetString(GL_EXTENSIONS); - Vector<String> strings = String(gl_extensions).split(" ", false); - for (int i = 0; i < strings.size(); i++) { - config.extensions.insert(strings[i]); + + const GLubyte *extension_string = glGetString(GL_EXTENSIONS); + + Vector<String> extensions = String((const char *)extension_string).split(" "); + + for (int i = 0; i < extensions.size(); i++) { + config.extensions.insert(extensions[i]); } } + config.shrink_textures_x2 = false; + config.float_texture_supported = config.extensions.find("GL_ARB_texture_float") != NULL || config.extensions.find("GL_OES_texture_float") != NULL; + config.s3tc_supported = config.extensions.find("GL_EXT_texture_compression_s3tc") != NULL; + config.etc1_supported = config.extensions.has("GL_OES_compressed_ETC1_RGB8_texture") != NULL; + frame.count = 0; - frame.prev_tick = 0; frame.delta = 0; frame.current_rt = NULL; frame.clear_request = false; // config.keep_original_textures = false; - glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &config.max_texture_image_units); + glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &config.max_texture_image_units); glGetIntegerv(GL_MAX_TEXTURE_SIZE, &config.max_texture_size); shaders.copy.init(); + shaders.cubemap_filter.init(); + + { + // quad for copying stuff + + glGenBuffers(1, &resources.quadie); + glBindBuffer(GL_ARRAY_BUFFER, resources.quadie); + { + const float qv[16] = { + -1, + -1, + 0, + 0, + -1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + -1, + 1, + 0, + }; + + glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 16, qv, GL_STATIC_DRAW); + } + + glBindBuffer(GL_ARRAY_BUFFER, 0); + } { //default textures @@ -2066,14 +4179,55 @@ void RasterizerStorageGLES2::initialize() { glGenerateMipmap(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, 0); } + + // skeleton buffer + { + resources.skeleton_transform_buffer_size = 0; + glGenBuffers(1, &resources.skeleton_transform_buffer); + } + + // radical inverse vdc cache texture + // used for cubemap filtering + if (config.float_texture_supported) { + glGenTextures(1, &resources.radical_inverse_vdc_cache_tex); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, resources.radical_inverse_vdc_cache_tex); + + float radical_inverse[512]; + + for (uint32_t i = 0; i < 512; i++) { + uint32_t bits = i; + + bits = (bits << 16) | (bits >> 16); + bits = ((bits & 0x55555555) << 1) | ((bits & 0xAAAAAAAA) >> 1); + bits = ((bits & 0x33333333) << 2) | ((bits & 0xCCCCCCCC) >> 2); + bits = ((bits & 0x0F0F0F0F) << 4) | ((bits & 0xF0F0F0F0) >> 4); + bits = ((bits & 0x00FF00FF) << 8) | ((bits & 0xFF00FF00) >> 8); + + float value = float(bits) * 2.3283064365386963e-10; + + radical_inverse[i] = value; + } + + glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, 512, 1, 0, GL_LUMINANCE, GL_FLOAT, radical_inverse); + + glBindTexture(GL_TEXTURE_2D, 0); + } } void RasterizerStorageGLES2::finalize() { } +void RasterizerStorageGLES2::_copy_screen() { + bind_quad_array(); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); +} + void RasterizerStorageGLES2::update_dirty_resources() { update_dirty_shaders(); update_dirty_materials(); + update_dirty_skeletons(); } RasterizerStorageGLES2::RasterizerStorageGLES2() { diff --git a/drivers/gles2/rasterizer_storage_gles2.h b/drivers/gles2/rasterizer_storage_gles2.h index df8c2fcf47..88783d7160 100644 --- a/drivers/gles2/rasterizer_storage_gles2.h +++ b/drivers/gles2/rasterizer_storage_gles2.h @@ -30,6 +30,7 @@ #ifndef RASTERIZERSTORAGEGLES2_H #define RASTERIZERSTORAGEGLES2_H +#include "dvector.h" #include "self_list.h" #include "servers/visual/rasterizer.h" #include "servers/visual/shader_language.h" @@ -37,11 +38,11 @@ #include "shader_gles2.h" #include "shaders/copy.glsl.gen.h" +#include "shaders/cubemap_filter.glsl.gen.h" /* #include "shaders/blend_shape.glsl.gen.h" #include "shaders/canvas.glsl.gen.h" #include "shaders/copy.glsl.gen.h" -#include "shaders/cubemap_filter.glsl.gen.h" #include "shaders/particles.glsl.gen.h" */ @@ -76,6 +77,10 @@ public: Set<String> extensions; + bool float_texture_supported; + bool s3tc_supported; + bool etc1_supported; + bool keep_original_textures; bool no_depth_prepass; @@ -89,8 +94,13 @@ public: GLuint normal_tex; GLuint aniso_tex; + GLuint radical_inverse_vdc_cache_tex; + GLuint quadie; - GLuint quadie_array; + + size_t skeleton_transform_buffer_size; + GLuint skeleton_transform_buffer; + PoolVector<float> skeleton_transform_cpu_buffer; } resources; @@ -99,6 +109,7 @@ public: ShaderCompilerGLES2 compiler; CopyShaderGLES2 copy; + CubemapFilterShaderGLES2 cubemap_filter; ShaderCompilerGLES2::IdentifierActions actions_canvas; ShaderCompilerGLES2::IdentifierActions actions_scene; @@ -139,10 +150,72 @@ public: } info; + void bind_quad_array() const; + ///////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////DATA/////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////// + struct Instanciable : public RID_Data { + SelfList<RasterizerScene::InstanceBase>::List instance_list; + + _FORCE_INLINE_ void instance_change_notify() { + SelfList<RasterizerScene::InstanceBase> *instances = instance_list.first(); + + while (instances) { + instances->self()->base_changed(); + instances = instances->next(); + } + } + + _FORCE_INLINE_ void instance_material_change_notify() { + SelfList<RasterizerScene::InstanceBase> *instances = instance_list.first(); + + while (instances) { + instances->self()->base_material_changed(); + instances = instances->next(); + } + } + + _FORCE_INLINE_ void instance_remove_deps() { + SelfList<RasterizerScene::InstanceBase> *instances = instance_list.first(); + + while (instances) { + instances->self()->base_removed(); + instances = instances->next(); + } + } + + Instanciable() {} + + virtual ~Instanciable() {} + }; + + struct GeometryOwner : public Instanciable { + }; + + struct Geometry : public Instanciable { + + enum Type { + GEOMETRY_INVALID, + GEOMETRY_SURFACE, + GEOMETRY_IMMEDIATE, + GEOMETRY_MULTISURFACE + }; + + Type type; + RID material; + uint64_t last_pass; + uint32_t index; + + virtual void material_changed_notify() {} + + Geometry() { + last_pass = 0; + index = 0; + } + }; + ///////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////API//////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////// @@ -158,9 +231,10 @@ public: String path; uint32_t flags; - int width, height; + int width, height, depth; int alloc_width, alloc_height; Image::Format format; + VS::TextureType type; GLenum target; GLenum gl_format_cache; @@ -171,6 +245,10 @@ public: int total_data_size; bool ignore_mipmaps; + bool compressed; + + bool srgb; + int mipmaps; bool active; @@ -180,7 +258,18 @@ public: RenderTarget *render_target; - Ref<Image> images[6]; + Vector<Ref<Image> > images; + + bool redraw_if_visible; + + VisualServer::TextureDetectCallback detect_3d; + void *detect_3d_ud; + + VisualServer::TextureDetectCallback detect_srgb; + void *detect_srgb_ud; + + VisualServer::TextureDetectCallback detect_normal; + void *detect_normal_ud; Texture() { flags = 0; @@ -196,6 +285,8 @@ public: total_data_size = 0; ignore_mipmaps = false; + compressed = false; + active = false; tex_id = 0; @@ -205,6 +296,8 @@ public: proxy = NULL; render_target = NULL; + + redraw_if_visible = false; } _ALWAYS_INLINE_ Texture *get_ptr() { @@ -232,20 +325,22 @@ public: mutable RID_Owner<Texture> texture_owner; - Ref<Image> _get_gl_image_and_format(const Ref<Image> &p_image, Image::Format p_format, uint32_t p_flags, GLenum &r_gl_format, GLenum &r_gl_internal_format, GLenum &r_gl_type); + Ref<Image> _get_gl_image_and_format(const Ref<Image> &p_image, Image::Format p_format, uint32_t p_flags, Image::Format &r_real_format, GLenum &r_gl_format, GLenum &r_gl_internal_format, GLenum &r_gl_type, bool &r_compressed) const; virtual RID texture_create(); - virtual void texture_allocate(RID p_texture, int p_width, int p_height, Image::Format p_format, uint32_t p_flags = VS::TEXTURE_FLAGS_DEFAULT); - virtual void texture_set_data(RID p_texture, const Ref<Image> &p_image, VS::CubeMapSide p_cube_side = VS::CUBEMAP_LEFT); - virtual void texture_set_data_partial(RID p_texture, const Ref<Image> &p_image, int src_x, int src_y, int src_w, int src_h, int dst_x, int dst_y, int p_dst_mip, VS::CubeMapSide p_cube_side = VS::CUBEMAP_LEFT); - virtual Ref<Image> texture_get_data(RID p_texture, VS::CubeMapSide p_cube_side = VS::CUBEMAP_LEFT) const; + virtual void texture_allocate(RID p_texture, int p_width, int p_height, int p_depth_3d, Image::Format p_format, VS::TextureType p_type, uint32_t p_flags = VS::TEXTURE_FLAGS_DEFAULT); + virtual void texture_set_data(RID p_texture, const Ref<Image> &p_image, int p_layer = 0); + virtual void texture_set_data_partial(RID p_texture, const Ref<Image> &p_image, int src_x, int src_y, int src_w, int src_h, int dst_x, int dst_y, int p_dst_mip, int p_layer = 0); + virtual Ref<Image> texture_get_data(RID p_texture, int p_layer = 0) const; virtual void texture_set_flags(RID p_texture, uint32_t p_flags); virtual uint32_t texture_get_flags(RID p_texture) const; virtual Image::Format texture_get_format(RID p_texture) const; + virtual VS::TextureType texture_get_type(RID p_texture) const; virtual uint32_t texture_get_texid(RID p_texture) const; virtual uint32_t texture_get_width(RID p_texture) const; virtual uint32_t texture_get_height(RID p_texture) const; - virtual void texture_set_size_override(RID p_texture, int p_width, int p_height); + virtual uint32_t texture_get_depth(RID p_texture) const; + virtual void texture_set_size_override(RID p_texture, int p_width, int p_height, int p_depth); virtual void texture_set_path(RID p_texture, const String &p_path); virtual String texture_get_path(RID p_texture) const; @@ -264,8 +359,19 @@ public: virtual void texture_set_detect_srgb_callback(RID p_texture, VisualServer::TextureDetectCallback p_callback, void *p_userdata); virtual void texture_set_detect_normal_callback(RID p_texture, VisualServer::TextureDetectCallback p_callback, void *p_userdata); + virtual void texture_set_force_redraw_if_visible(RID p_texture, bool p_enable); + /* SKY API */ + struct Sky : public RID_Data { + + RID panorama; + GLuint radiance; + int radiance_size; + }; + + mutable RID_Owner<Sky> sky_owner; + virtual RID sky_create(); virtual void sky_set_texture(RID p_sky, RID p_panorama, int p_radiance_size); @@ -326,7 +432,6 @@ public: } canvas_item; - /* struct Spatial { enum BlendMode { @@ -363,6 +468,7 @@ public: bool uses_discard; bool uses_sss; bool uses_screen_texture; + bool uses_depth_texture; bool uses_time; bool writes_modelview_or_projection; bool uses_vertex_lighting; @@ -373,7 +479,6 @@ public: struct Particles { } particles; - */ bool uses_vertex_time; bool uses_fragment_time; @@ -413,7 +518,7 @@ public: Map<StringName, Variant> params; SelfList<Material> list; SelfList<Material> dirty_list; - Vector<RID> textures; + Vector<Pair<StringName, RID> > textures; float line_width; int render_priority; @@ -443,6 +548,11 @@ public: mutable SelfList<Material>::List _material_dirty_list; void _material_make_dirty(Material *p_material) const; + void _material_add_geometry(RID p_material, Geometry *p_geometry); + void _material_remove_geometry(RID p_material, Geometry *p_geometry); + + void _update_material(Material *p_material); + mutable RID_Owner<Material> material_owner; virtual RID material_create(); @@ -467,6 +577,109 @@ public: void update_dirty_materials(); /* MESH API */ + + struct Mesh; + + struct Surface : public Geometry { + + struct Attrib { + bool enabled; + bool integer; + GLuint index; + GLint size; + GLenum type; + GLboolean normalized; + GLsizei stride; + uint32_t offset; + }; + + Attrib attribs[VS::ARRAY_MAX]; + + Mesh *mesh; + uint32_t format; + + GLuint vertex_id; + GLuint index_id; + + struct BlendShape { + GLuint vertex_id; + GLuint array_id; + }; + + Vector<BlendShape> blend_shapes; + + AABB aabb; + + int array_len; + int index_array_len; + int max_bone; + + int array_byte_size; + int index_array_byte_size; + + VS::PrimitiveType primitive; + + Vector<AABB> skeleton_bone_aabb; + Vector<bool> skeleton_bone_used; + + bool active; + + PoolVector<uint8_t> data; + PoolVector<uint8_t> index_data; + + int total_data_size; + + Surface() { + array_byte_size = 0; + index_array_byte_size = 0; + + array_len = 0; + index_array_len = 0; + + mesh = NULL; + + primitive = VS::PRIMITIVE_POINTS; + + active = false; + + total_data_size = 0; + } + }; + + struct MultiMesh; + + struct Mesh : public GeometryOwner { + + bool active; + + Vector<Surface *> surfaces; + + int blend_shape_count; + VS::BlendShapeMode blend_shape_mode; + + AABB custom_aabb; + + mutable uint64_t last_pass; + + SelfList<MultiMesh>::List multimeshes; + + _FORCE_INLINE_ void update_multimeshes() { + SelfList<MultiMesh> *mm = multimeshes.first(); + + while (mm) { + mm->self()->instance_material_change_notify(); + mm = mm->next(); + } + } + + Mesh() { + blend_shape_mode = VS::BLEND_SHAPE_MODE_NORMALIZED; + blend_shape_count = 0; + } + }; + + mutable RID_Owner<Mesh> mesh_owner; + virtual RID mesh_create(); virtual void mesh_add_surface(RID p_mesh, uint32_t p_format, VS::PrimitiveType p_primitive, const PoolVector<uint8_t> &p_array, int p_vertex_count, const PoolVector<uint8_t> &p_index_array, int p_index_count, const AABB &p_aabb, const Vector<PoolVector<uint8_t> > &p_blend_shapes = Vector<PoolVector<uint8_t> >(), const Vector<AABB> &p_bone_aabbs = Vector<AABB>()); @@ -506,16 +719,65 @@ public: /* MULTIMESH API */ + struct MultiMesh : public GeometryOwner { + + RID mesh; + int size; + + VS::MultimeshTransformFormat transform_format; + VS::MultimeshColorFormat color_format; + VS::MultimeshCustomDataFormat custom_data_format; + + Vector<float> data; + + AABB aabb; + + SelfList<MultiMesh> update_list; + SelfList<MultiMesh> mesh_list; + + int visible_instances; + + int xform_floats; + int color_floats; + int custom_data_floats; + + bool dirty_aabb; + bool dirty_data; + + MultiMesh() : + update_list(this), + mesh_list(this) { + dirty_aabb = true; + dirty_data = true; + + xform_floats = 0; + color_floats = 0; + custom_data_floats = 0; + + visible_instances = -1; + + size = 0; + + transform_format = VS::MULTIMESH_TRANSFORM_2D; + color_format = VS::MULTIMESH_COLOR_NONE; + custom_data_format = VS::MULTIMESH_CUSTOM_DATA_NONE; + } + }; + + mutable RID_Owner<MultiMesh> multimesh_owner; + + SelfList<MultiMesh>::List multimesh_update_list; + virtual RID multimesh_create(); - virtual void multimesh_allocate(RID p_multimesh, int p_instances, VS::MultimeshTransformFormat p_transform_format, VS::MultimeshColorFormat p_color_format,VS::MultimeshCustomDataFormat p_data=VS::MULTIMESH_CUSTOM_DATA_NONE); + virtual void multimesh_allocate(RID p_multimesh, int p_instances, VS::MultimeshTransformFormat p_transform_format, VS::MultimeshColorFormat p_color_format, VS::MultimeshCustomDataFormat p_data = VS::MULTIMESH_CUSTOM_DATA_NONE); virtual int multimesh_get_instance_count(RID p_multimesh) const; virtual void multimesh_set_mesh(RID p_multimesh, RID p_mesh); virtual void multimesh_instance_set_transform(RID p_multimesh, int p_index, const Transform &p_transform); virtual void multimesh_instance_set_transform_2d(RID p_multimesh, int p_index, const Transform2D &p_transform); virtual void multimesh_instance_set_color(RID p_multimesh, int p_index, const Color &p_color); - virtual void multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_color); + virtual void multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_custom_data); virtual RID multimesh_get_mesh(RID p_multimesh) const; @@ -535,8 +797,40 @@ public: /* IMMEDIATE API */ + struct Immediate : public Geometry { + + struct Chunk { + RID texture; + VS::PrimitiveType primitive; + Vector<Vector3> vertices; + Vector<Vector3> normals; + Vector<Plane> tangents; + Vector<Color> colors; + Vector<Vector2> uvs; + Vector<Vector2> uv2s; + }; + + List<Chunk> chunks; + bool building; + int mask; + AABB aabb; + + Immediate() { + type = GEOMETRY_IMMEDIATE; + building = false; + } + }; + + Vector3 chunk_normal; + Plane chunk_tangent; + Color chunk_color; + Vector2 chunk_uv; + Vector2 chunk_uv2; + + mutable RID_Owner<Immediate> immediate_owner; + virtual RID immediate_create(); - virtual void immediate_begin(RID p_immediate, VS::PrimitiveType p_rimitive, RID p_texture = RID()); + virtual void immediate_begin(RID p_immediate, VS::PrimitiveType p_primitive, RID p_texture = RID()); virtual void immediate_vertex(RID p_immediate, const Vector3 &p_vertex); virtual void immediate_normal(RID p_immediate, const Vector3 &p_normal); virtual void immediate_tangent(RID p_immediate, const Plane &p_tangent); @@ -551,6 +845,33 @@ public: /* SKELETON API */ + struct Skeleton : RID_Data { + + bool use_2d; + + int size; + + // TODO use float textures for storage + + Vector<float> bone_data; + + GLuint tex_id; + + SelfList<Skeleton> update_list; + Set<RasterizerScene::InstanceBase *> instances; + + Skeleton() : + update_list(this) { + tex_id = 0; + size = 0; + use_2d = false; + } + }; + + mutable RID_Owner<Skeleton> skeleton_owner; + + SelfList<Skeleton>::List skeleton_update_list; + void update_dirty_skeletons(); virtual RID skeleton_create(); @@ -562,8 +883,38 @@ public: virtual Transform2D skeleton_bone_get_transform_2d(RID p_skeleton, int p_bone) const; virtual void skeleton_set_base_transform_2d(RID p_skeleton, const Transform2D &p_base_transform); + void _update_skeleton_transform_buffer(const PoolVector<float> &p_data, size_t p_size); + /* Light API */ + struct Light : Instanciable { + VS::LightType type; + float param[VS::LIGHT_PARAM_MAX]; + + Color color; + Color shadow_color; + + RID projector; + + bool shadow; + bool negative; + bool reverse_cull; + + uint32_t cull_mask; + + VS::LightOmniShadowMode omni_shadow_mode; + VS::LightOmniShadowDetail omni_shadow_detail; + + VS::LightDirectionalShadowMode directional_shadow_mode; + VS::LightDirectionalShadowDepthRangeMode directional_range_mode; + + bool directional_blend_splits; + + uint64_t version; + }; + + mutable RID_Owner<Light> light_owner; + virtual RID light_create(VS::LightType p_type); virtual void light_set_color(RID p_light, const Color &p_color); @@ -818,7 +1169,6 @@ public: int canvas_draw_commands; float time[4]; float delta; - uint64_t prev_tick; uint64_t count; } frame; @@ -826,6 +1176,8 @@ public: void initialize(); void finalize(); + void _copy_screen(); + virtual bool has_os_feature(const String &p_feature) const; virtual void update_dirty_resources(); diff --git a/drivers/gles2/shader_compiler_gles2.cpp b/drivers/gles2/shader_compiler_gles2.cpp index aa55e72083..5ac2af6e5c 100644 --- a/drivers/gles2/shader_compiler_gles2.cpp +++ b/drivers/gles2/shader_compiler_gles2.cpp @@ -325,10 +325,10 @@ String ShaderCompilerGLES2::_dump_node_code(SL::Node *p_node, int p_level, Gener uniform_code += ";\n"; if (SL::is_sampler_type(E->get().type)) { - r_gen_code.texture_uniforms[E->get().texture_order] = _mkid(E->key()); - r_gen_code.texture_hints[E->get().texture_order] = E->get().hint; + r_gen_code.texture_uniforms.write[E->get().texture_order] = E->key(); + r_gen_code.texture_hints.write[E->get().texture_order] = E->get().hint; } else { - r_gen_code.uniforms[E->get().order] = E->key(); + r_gen_code.uniforms.write[E->get().order] = E->key(); } vertex_global += uniform_code.as_string(); @@ -507,7 +507,6 @@ String ShaderCompilerGLES2::_dump_node_code(SL::Node *p_node, int p_level, Gener case SL::OP_ASSIGN_DIV: case SL::OP_ASSIGN_SHIFT_LEFT: case SL::OP_ASSIGN_SHIFT_RIGHT: - case SL::OP_ASSIGN_MOD: case SL::OP_ASSIGN_BIT_AND: case SL::OP_ASSIGN_BIT_OR: case SL::OP_ASSIGN_BIT_XOR: { @@ -518,6 +517,16 @@ String ShaderCompilerGLES2::_dump_node_code(SL::Node *p_node, int p_level, Gener code += _dump_node_code(op_node->arguments[1], p_level, r_gen_code, p_actions, p_default_actions, p_assigning); } break; + case SL::OP_ASSIGN_MOD: { + code += _dump_node_code(op_node->arguments[0], p_level, r_gen_code, p_actions, p_default_actions, true); + code += " = "; + code += "mod("; + code += _dump_node_code(op_node->arguments[0], p_level, r_gen_code, p_actions, p_default_actions, true); + code += ", "; + code += _dump_node_code(op_node->arguments[1], p_level, r_gen_code, p_actions, p_default_actions, p_assigning); + code += ")"; + } break; + case SL::OP_BIT_INVERT: case SL::OP_NEGATE: case SL::OP_NOT: @@ -552,6 +561,45 @@ String ShaderCompilerGLES2::_dump_node_code(SL::Node *p_node, int p_level, Gener code += "textureCube"; } + } else if (var_node->name == "textureLod") { + // emit texture call + + if (op_node->arguments[1]->get_datatype() == SL::TYPE_SAMPLER2D) { + code += "texture2DLod"; + } else if (op_node->arguments[1]->get_datatype() == SL::TYPE_SAMPLERCUBE) { + code += "textureCubeLod"; + } + + } else if (var_node->name == "mix") { + + switch (op_node->arguments[3]->get_datatype()) { + + case SL::TYPE_BVEC2: { + code += "select2"; + } break; + + case SL::TYPE_BVEC3: { + code += "select3"; + } break; + + case SL::TYPE_BVEC4: { + code += "select4"; + } break; + + case SL::TYPE_VEC2: + case SL::TYPE_VEC3: + case SL::TYPE_VEC4: + case SL::TYPE_FLOAT: { + + code += "mix"; + } break; + + default: { + SL::DataType type = op_node->arguments[3]->get_datatype(); + print_line(String("uhhhh invalid mix with type: ") + itos(type)); + } break; + } + } else if (p_default_actions.renames.has(var_node->name)) { code += p_default_actions.renames[var_node->name]; } else if (internal_functions.has(var_node->name)) { @@ -590,6 +638,15 @@ String ShaderCompilerGLES2::_dump_node_code(SL::Node *p_node, int p_level, Gener code += _dump_node_code(op_node->arguments[2], p_level, r_gen_code, p_actions, p_default_actions, p_assigning); } break; + case SL::OP_MOD: { + + code += "mod("; + code += _dump_node_code(op_node->arguments[0], p_level, r_gen_code, p_actions, p_default_actions, p_assigning); + code += ", "; + code += _dump_node_code(op_node->arguments[1], p_level, r_gen_code, p_actions, p_default_actions, p_assigning); + code += ")"; + } break; + default: { code += "("; code += _dump_node_code(op_node->arguments[0], p_level, r_gen_code, p_actions, p_default_actions, p_assigning); @@ -647,6 +704,10 @@ String ShaderCompilerGLES2::_dump_node_code(SL::Node *p_node, int p_level, Gener } code += ";\n"; } else if (cf_node->flow_op == SL::FLOW_OP_DISCARD) { + if (p_actions.usage_flag_pointers.has("DISCARD") && !used_flag_pointers.has("DISCARD")) { + *p_actions.usage_flag_pointers["DISCARD"] = true; + used_flag_pointers.insert("DISCARD"); + } code += "discard;"; } else if (cf_node->flow_op == SL::FLOW_OP_CONTINUE) { code += "continue;"; @@ -721,8 +782,6 @@ ShaderCompilerGLES2::ShaderCompilerGLES2() { actions[VS::SHADER_CANVAS_ITEM].renames["NORMAL"] = "normal"; actions[VS::SHADER_CANVAS_ITEM].renames["NORMALMAP"] = "normal_map"; actions[VS::SHADER_CANVAS_ITEM].renames["NORMALMAP_DEPTH"] = "normal_depth"; - actions[VS::SHADER_CANVAS_ITEM].renames["UV"] = "uv_interp"; - actions[VS::SHADER_CANVAS_ITEM].renames["COLOR"] = "color"; actions[VS::SHADER_CANVAS_ITEM].renames["TEXTURE"] = "color_texture"; actions[VS::SHADER_CANVAS_ITEM].renames["TEXTURE_PIXEL_SIZE"] = "color_texpixel_size"; actions[VS::SHADER_CANVAS_ITEM].renames["NORMAL_TEXTURE"] = "normal_texture"; @@ -736,7 +795,6 @@ ShaderCompilerGLES2::ShaderCompilerGLES2() { actions[VS::SHADER_CANVAS_ITEM].renames["LIGHT_HEIGHT"] = "light_height"; actions[VS::SHADER_CANVAS_ITEM].renames["LIGHT_COLOR"] = "light_color"; actions[VS::SHADER_CANVAS_ITEM].renames["LIGHT_UV"] = "light_uv"; - //actions[VS::SHADER_CANVAS_ITEM].renames["LIGHT_SHADOW_COLOR"]="light_shadow_color"; actions[VS::SHADER_CANVAS_ITEM].renames["LIGHT"] = "light"; actions[VS::SHADER_CANVAS_ITEM].renames["SHADOW_COLOR"] = "shadow_color"; @@ -754,10 +812,10 @@ ShaderCompilerGLES2::ShaderCompilerGLES2() { /** SPATIAL SHADER **/ actions[VS::SHADER_SPATIAL].renames["WORLD_MATRIX"] = "world_transform"; - actions[VS::SHADER_SPATIAL].renames["INV_CAMERA_MATRIX"] = "camera_inverse_matrix"; - actions[VS::SHADER_SPATIAL].renames["CAMERA_MATRIX"] = "camera_matrix"; + actions[VS::SHADER_SPATIAL].renames["INV_CAMERA_MATRIX"] = "camera_matrix"; + actions[VS::SHADER_SPATIAL].renames["CAMERA_MATRIX"] = "camera_inverse_matrix"; actions[VS::SHADER_SPATIAL].renames["PROJECTION_MATRIX"] = "projection_matrix"; - actions[VS::SHADER_SPATIAL].renames["INV_PROJECTION_MATRIX"] = "inv_projection_matrix"; + actions[VS::SHADER_SPATIAL].renames["INV_PROJECTION_MATRIX"] = "projection_inverse_matrix"; actions[VS::SHADER_SPATIAL].renames["MODELVIEW_MATRIX"] = "modelview"; actions[VS::SHADER_SPATIAL].renames["VERTEX"] = "vertex.xyz"; @@ -768,7 +826,8 @@ ShaderCompilerGLES2::ShaderCompilerGLES2() { actions[VS::SHADER_SPATIAL].renames["UV2"] = "uv2_interp"; actions[VS::SHADER_SPATIAL].renames["COLOR"] = "color_interp"; actions[VS::SHADER_SPATIAL].renames["POINT_SIZE"] = "gl_PointSize"; - //actions[VS::SHADER_SPATIAL].renames["INSTANCE_ID"]=ShaderLanguage::TYPE_INT; + // gl_InstanceID is not available in OpenGL ES 2.0 + actions[VS::SHADER_SPATIAL].renames["INSTANCE_ID"] = "0"; //builtins @@ -790,13 +849,11 @@ ShaderCompilerGLES2::ShaderCompilerGLES2() { actions[VS::SHADER_SPATIAL].renames["CLEARCOAT_GLOSS"] = "clearcoat_gloss"; actions[VS::SHADER_SPATIAL].renames["ANISOTROPY"] = "anisotropy"; actions[VS::SHADER_SPATIAL].renames["ANISOTROPY_FLOW"] = "anisotropy_flow"; - //actions[VS::SHADER_SPATIAL].renames["SSS_SPREAD"] = "sss_spread"; actions[VS::SHADER_SPATIAL].renames["SSS_STRENGTH"] = "sss_strength"; actions[VS::SHADER_SPATIAL].renames["TRANSMISSION"] = "transmission"; actions[VS::SHADER_SPATIAL].renames["AO"] = "ao"; actions[VS::SHADER_SPATIAL].renames["AO_LIGHT_AFFECT"] = "ao_light_affect"; actions[VS::SHADER_SPATIAL].renames["EMISSION"] = "emission"; - //actions[VS::SHADER_SPATIAL].renames["SCREEN_UV"]=ShaderLanguage::TYPE_VEC2; actions[VS::SHADER_SPATIAL].renames["POINT_COORD"] = "gl_PointCoord"; actions[VS::SHADER_SPATIAL].renames["INSTANCE_CUSTOM"] = "instance_custom"; actions[VS::SHADER_SPATIAL].renames["SCREEN_UV"] = "screen_uv"; @@ -838,8 +895,6 @@ ShaderCompilerGLES2::ShaderCompilerGLES2() { actions[VS::SHADER_SPATIAL].usage_defines["DIFFUSE_LIGHT"] = "#define USE_LIGHT_SHADER_CODE\n"; actions[VS::SHADER_SPATIAL].usage_defines["SPECULAR_LIGHT"] = "#define USE_LIGHT_SHADER_CODE\n"; - actions[VS::SHADER_SPATIAL].renames["SSS_STRENGTH"] = "sss_strength"; - actions[VS::SHADER_SPATIAL].render_mode_defines["skip_vertex_transform"] = "#define SKIP_TRANSFORM_USED\n"; actions[VS::SHADER_SPATIAL].render_mode_defines["world_vertex_coords"] = "#define VERTEX_WORLD_COORDS_USED\n"; diff --git a/drivers/gles2/shader_gles2.cpp b/drivers/gles2/shader_gles2.cpp index fa9562877d..5cdbdf84e0 100644 --- a/drivers/gles2/shader_gles2.cpp +++ b/drivers/gles2/shader_gles2.cpp @@ -33,7 +33,10 @@ #include "print_string.h" #include "string_builder.h" -//#define DEBUG_OPENGL +#include "rasterizer_gles2.h" +#include "rasterizer_storage_gles2.h" + +// #define DEBUG_OPENGL // #include "shaders/copy.glsl.gen.h" @@ -54,7 +57,7 @@ ShaderGLES2 *ShaderGLES2::active = NULL; -//#define DEBUG_SHADER +// #define DEBUG_SHADER #ifdef DEBUG_SHADER @@ -83,7 +86,10 @@ void ShaderGLES2::bind_uniforms() { continue; } - const Variant &v = E->value(); + Variant v; + + v = E->value(); + _set_uniform_variant(location, v); E = E->next(); } @@ -128,6 +134,28 @@ bool ShaderGLES2::bind() { glUseProgram(version->id); + // find out uniform names and locations + + int count; + glGetProgramiv(version->id, GL_ACTIVE_UNIFORMS, &count); + version->uniform_names.resize(count); + + for (int i = 0; i < count; i++) { + GLchar uniform_name[1024]; + int len = 0; + GLint size = 0; + GLenum type; + + glGetActiveUniform(version->id, i, 1024, &len, &size, &type, uniform_name); + + uniform_name[len] = '\0'; + String name = String((const char *)uniform_name); + + version->uniform_names.write[i] = name; + } + + bind_uniforms(); + DEBUG_TEST_ERROR("use program"); active = this; @@ -228,7 +256,7 @@ ShaderGLES2::Version *ShaderGLES2::get_current_version() { } if (!_v) - version_map[conditional_version]; + version_map[conditional_version] = Version(); Version &v = version_map[conditional_version]; @@ -288,7 +316,7 @@ ShaderGLES2::Version *ShaderGLES2::get_current_version() { if (cc) { for (int i = 0; i < cc->custom_defines.size(); i++) { - strings.push_back(cc->custom_defines[i]); + strings.push_back(cc->custom_defines.write[i]); DEBUG_PRINT("CD #" + itos(i) + ": " + String(cc->custom_defines[i])); } } @@ -389,6 +417,10 @@ ShaderGLES2::Version *ShaderGLES2::get_current_version() { strings.push_back(fragment_code3.get_data()); #ifdef DEBUG_SHADER + + if (cc) { + DEBUG_PRINT("\nFragment Code:\n\n" + String(cc->fragment_globals)); + } DEBUG_PRINT("\nFragment Code:\n\n" + String(code_string.get_data())); #endif @@ -495,14 +527,28 @@ ShaderGLES2::Version *ShaderGLES2::get_current_version() { for (int i = 0; i < texunit_pair_count; i++) { GLint loc = glGetUniformLocation(v.id, texunit_pairs[i].name); - if (loc >= 0) - glUniform1i(loc, texunit_pairs[i].index); + if (loc >= 0) { + if (texunit_pairs[i].index < 0) { + glUniform1i(loc, max_image_units + texunit_pairs[i].index); + } else { + glUniform1i(loc, texunit_pairs[i].index); + } + } } if (cc) { - v.custom_uniform_locations.resize(cc->custom_uniforms.size()); + // uniforms for (int i = 0; i < cc->custom_uniforms.size(); i++) { - v.custom_uniform_locations[i] = glGetUniformLocation(v.id, String(cc->custom_uniforms[i]).ascii().get_data()); + StringName native_uniform_name = "m_" + cc->custom_uniforms[i]; + GLint location = glGetUniformLocation(v.id, ((String)native_uniform_name).ascii().get_data()); + v.custom_uniform_locations[cc->custom_uniforms[i]] = location; + } + + // textures + for (int i = 0; i < cc->texture_uniforms.size(); i++) { + StringName native_uniform_name = "m_" + cc->texture_uniforms[i]; + GLint location = glGetUniformLocation(v.id, ((String)native_uniform_name).ascii().get_data()); + v.custom_uniform_locations[cc->texture_uniforms[i]] = location; } } @@ -602,6 +648,8 @@ void ShaderGLES2::setup( } } } + + glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &max_image_units); } void ShaderGLES2::finish() { @@ -660,6 +708,7 @@ void ShaderGLES2::set_custom_shader_code(uint32_t p_code_id, cc->light = p_light; cc->custom_uniforms = p_uniforms; cc->custom_defines = p_custom_defines; + cc->texture_uniforms = p_texture_uniforms; cc->version++; } @@ -675,6 +724,367 @@ void ShaderGLES2::free_custom_shader(uint32_t p_code_id) { custom_code_map.erase(p_code_id); } +void ShaderGLES2::use_material(void *p_material) { + RasterizerStorageGLES2::Material *material = (RasterizerStorageGLES2::Material *)p_material; + + if (!material) { + return; + } + + if (!material->shader) { + return; + } + + Version *v = version_map.getptr(conditional_version); + + CustomCode *cc = NULL; + if (v) { + cc = custom_code_map.getptr(v->code_version); + } + + // bind uniforms + for (Map<StringName, ShaderLanguage::ShaderNode::Uniform>::Element *E = material->shader->uniforms.front(); E; E = E->next()) { + + if (E->get().texture_order >= 0) + continue; // this is a texture, doesn't go here + + Map<StringName, Variant>::Element *V = material->params.find(E->key()); + + Pair<ShaderLanguage::DataType, Vector<ShaderLanguage::ConstantNode::Value> > value; + + value.first = E->get().type; + value.second = E->get().default_value; + + if (V) { + value.second = Vector<ShaderLanguage::ConstantNode::Value>(); + value.second.resize(E->get().default_value.size()); + switch (E->get().type) { + case ShaderLanguage::TYPE_BOOL: { + if (value.second.size() < 1) + value.second.resize(1); + value.second.write[0].boolean = V->get(); + } break; + + case ShaderLanguage::TYPE_BVEC2: { + if (value.second.size() < 2) + value.second.resize(2); + int flags = V->get(); + value.second.write[0].boolean = flags & 1; + value.second.write[1].boolean = flags & 2; + } break; + + case ShaderLanguage::TYPE_BVEC3: { + if (value.second.size() < 3) + value.second.resize(3); + int flags = V->get(); + value.second.write[0].boolean = flags & 1; + value.second.write[1].boolean = flags & 2; + value.second.write[2].boolean = flags & 4; + + } break; + + case ShaderLanguage::TYPE_BVEC4: { + if (value.second.size() < 4) + value.second.resize(4); + int flags = V->get(); + value.second.write[0].boolean = flags & 1; + value.second.write[1].boolean = flags & 2; + value.second.write[2].boolean = flags & 4; + value.second.write[3].boolean = flags & 8; + + } break; + + case ShaderLanguage::TYPE_INT: { + if (value.second.size() < 1) + value.second.resize(1); + int val = V->get(); + value.second.write[0].sint = val; + } break; + + case ShaderLanguage::TYPE_IVEC2: { + if (value.second.size() < 2) + value.second.resize(2); + PoolIntArray val = V->get(); + for (int i = 0; i < val.size(); i++) { + value.second.write[i].sint = val[i]; + } + } break; + + case ShaderLanguage::TYPE_IVEC3: { + if (value.second.size() < 3) + value.second.resize(3); + PoolIntArray val = V->get(); + for (int i = 0; i < val.size(); i++) { + value.second.write[i].sint = val[i]; + } + + } break; + + case ShaderLanguage::TYPE_IVEC4: { + if (value.second.size() < 4) + value.second.resize(4); + PoolIntArray val = V->get(); + for (int i = 0; i < val.size(); i++) { + value.second.write[i].sint = val[i]; + } + + } break; + + case ShaderLanguage::TYPE_UINT: { + if (value.second.size() < 1) + value.second.resize(1); + uint32_t val = V->get(); + value.second.write[0].uint = val; + } break; + + case ShaderLanguage::TYPE_UVEC2: { + if (value.second.size() < 2) + value.second.resize(2); + PoolIntArray val = V->get(); + for (int i = 0; i < val.size(); i++) { + value.second.write[i].uint = val[i]; + } + + } break; + + case ShaderLanguage::TYPE_UVEC3: { + if (value.second.size() < 3) + value.second.resize(3); + PoolIntArray val = V->get(); + for (int i = 0; i < val.size(); i++) { + value.second.write[i].uint = val[i]; + } + + } break; + + case ShaderLanguage::TYPE_UVEC4: { + if (value.second.size() < 4) + value.second.resize(4); + PoolIntArray val = V->get(); + for (int i = 0; i < val.size(); i++) { + value.second.write[i].uint = val[i]; + } + + } break; + + case ShaderLanguage::TYPE_FLOAT: { + if (value.second.size() < 1) + value.second.resize(1); + value.second.write[0].real = V->get(); + + } break; + + case ShaderLanguage::TYPE_VEC2: { + if (value.second.size() < 2) + value.second.resize(2); + Vector2 val = V->get(); + value.second.write[0].real = val.x; + value.second.write[1].real = val.y; + } break; + + case ShaderLanguage::TYPE_VEC3: { + if (value.second.size() < 3) + value.second.resize(3); + Vector3 val = V->get(); + value.second.write[0].real = val.x; + value.second.write[1].real = val.y; + value.second.write[2].real = val.z; + } break; + + case ShaderLanguage::TYPE_VEC4: { + if (value.second.size() < 4) + value.second.resize(4); + if (V->get().get_type() == Variant::PLANE) { + Plane val = V->get(); + value.second.write[0].real = val.normal.x; + value.second.write[1].real = val.normal.y; + value.second.write[2].real = val.normal.z; + value.second.write[3].real = val.d; + } else { + Color val = V->get(); + value.second.write[0].real = val.r; + value.second.write[1].real = val.g; + value.second.write[2].real = val.b; + value.second.write[3].real = val.a; + } + + } break; + + case ShaderLanguage::TYPE_MAT2: { + Transform2D val = V->get(); + + if (value.second.size() < 4) { + value.second.resize(4); + } + + value.second.write[0].real = val.elements[0][0]; + value.second.write[1].real = val.elements[0][1]; + value.second.write[2].real = val.elements[1][0]; + value.second.write[3].real = val.elements[1][1]; + + } break; + + case ShaderLanguage::TYPE_MAT3: { + Basis val = V->get(); + + if (value.second.size() < 9) { + value.second.resize(9); + } + + value.second.write[0].real = val.elements[0][0]; + value.second.write[1].real = val.elements[0][1]; + value.second.write[2].real = val.elements[0][2]; + value.second.write[3].real = val.elements[1][0]; + value.second.write[4].real = val.elements[1][1]; + value.second.write[5].real = val.elements[1][2]; + value.second.write[6].real = val.elements[2][0]; + value.second.write[7].real = val.elements[2][1]; + value.second.write[8].real = val.elements[2][2]; + } break; + + case ShaderLanguage::TYPE_MAT4: { + Transform val = V->get(); + + if (value.second.size() < 16) { + value.second.resize(16); + } + + value.second.write[0].real = val.basis.elements[0][0]; + value.second.write[1].real = val.basis.elements[0][1]; + value.second.write[2].real = val.basis.elements[0][2]; + value.second.write[3].real = 0; + value.second.write[4].real = val.basis.elements[1][0]; + value.second.write[5].real = val.basis.elements[1][1]; + value.second.write[6].real = val.basis.elements[1][2]; + value.second.write[7].real = 0; + value.second.write[8].real = val.basis.elements[2][0]; + value.second.write[9].real = val.basis.elements[2][1]; + value.second.write[10].real = val.basis.elements[2][2]; + value.second.write[11].real = 0; + value.second.write[12].real = val.origin[0]; + value.second.write[13].real = val.origin[1]; + value.second.write[14].real = val.origin[2]; + value.second.write[15].real = 1; + } break; + + default: { + + } break; + } + } else { + if (value.second.size() == 0) { + // No default value set... weird, let's just use zero for everything + size_t default_arg_size = 1; + bool is_float = false; + switch (E->get().type) { + case ShaderLanguage::TYPE_BOOL: + case ShaderLanguage::TYPE_INT: + case ShaderLanguage::TYPE_UINT: { + default_arg_size = 1; + } break; + + case ShaderLanguage::TYPE_FLOAT: { + default_arg_size = 1; + is_float = true; + } break; + + case ShaderLanguage::TYPE_BVEC2: + case ShaderLanguage::TYPE_IVEC2: + case ShaderLanguage::TYPE_UVEC2: { + default_arg_size = 2; + } break; + + case ShaderLanguage::TYPE_VEC2: { + default_arg_size = 2; + is_float = true; + } break; + + case ShaderLanguage::TYPE_BVEC3: + case ShaderLanguage::TYPE_IVEC3: + case ShaderLanguage::TYPE_UVEC3: { + default_arg_size = 3; + } break; + + case ShaderLanguage::TYPE_VEC3: { + default_arg_size = 3; + is_float = true; + } break; + + case ShaderLanguage::TYPE_BVEC4: + case ShaderLanguage::TYPE_IVEC4: + case ShaderLanguage::TYPE_UVEC4: { + default_arg_size = 4; + } break; + + case ShaderLanguage::TYPE_VEC4: { + default_arg_size = 4; + is_float = true; + } break; + + default: { + // TODO matricies and all that stuff + default_arg_size = 1; + } break; + } + + value.second.resize(default_arg_size); + + for (int i = 0; i < default_arg_size; i++) { + if (is_float) { + value.second.write[i].real = 0.0; + } else { + value.second.write[i].uint = 0; + } + } + } + } + + // GLint location = get_uniform_location(E->key()); + + GLint location; + if (v->custom_uniform_locations.has(E->key())) { + location = v->custom_uniform_locations[E->key()]; + } else { + int idx = v->uniform_names.find(E->key()); // TODO maybe put those in a Map? + if (idx < 0) { + location = -1; + } else { + location = v->uniform_location[idx]; + } + } + + _set_uniform_value(location, value); + } + + // bind textures + int tc = material->textures.size(); + Pair<StringName, RID> *textures = material->textures.ptrw(); + + ShaderLanguage::ShaderNode::Uniform::Hint *texture_hints = material->shader->texture_hints.ptrw(); + + for (int i = 0; i < tc; i++) { + + Pair<ShaderLanguage::DataType, Vector<ShaderLanguage::ConstantNode::Value> > value; + value.first = ShaderLanguage::TYPE_INT; + value.second.resize(1); + value.second.write[0].sint = i; + + // GLint location = get_uniform_location(textures[i].first); + + // if (location < 0) { + // location = material->shader->uniform_locations[textures[i].first]; + // } + GLint location = -1; + if (v->custom_uniform_locations.has(textures[i].first)) { + location = v->custom_uniform_locations[textures[i].first]; + } else { + location = get_uniform_location(textures[i].first); + } + + _set_uniform_value(location, value); + } +} + void ShaderGLES2::set_base_material_tex_index(int p_idx) { } diff --git a/drivers/gles2/shader_gles2.h b/drivers/gles2/shader_gles2.h index c3635bc201..cb515c199c 100644 --- a/drivers/gles2/shader_gles2.h +++ b/drivers/gles2/shader_gles2.h @@ -44,6 +44,11 @@ #include "map.h" #include "variant.h" +#include "core/pair.h" +#include "servers/visual/shader_language.h" + +class RasterizerStorageGLES2; + class ShaderGLES2 { protected: struct Enum { @@ -105,9 +110,10 @@ private: GLuint id; GLuint vert_id; GLuint frag_id; + Vector<StringName> uniform_names; GLint *uniform_location; Vector<GLint> texture_uniform_locations; - Vector<GLint> custom_uniform_locations; + Map<StringName, GLint> custom_uniform_locations; uint32_t code_version; bool ok; Version() { @@ -169,6 +175,168 @@ private: int max_image_units; + Map<uint32_t, Variant> uniform_defaults; + Map<uint32_t, CameraMatrix> uniform_cameras; + + Map<StringName, Pair<ShaderLanguage::DataType, Vector<ShaderLanguage::ConstantNode::Value> > > uniform_values; + +protected: + _FORCE_INLINE_ int _get_uniform(int p_which) const; + _FORCE_INLINE_ void _set_conditional(int p_which, bool p_value); + + void setup(const char **p_conditional_defines, + int p_conditional_count, + const char **p_uniform_names, + int p_uniform_count, + const AttributePair *p_attribute_pairs, + int p_attribute_count, + const TexUnitPair *p_texunit_pairs, + int p_texunit_pair_count, + const char *p_vertex_code, + const char *p_fragment_code, + int p_vertex_code_start, + int p_fragment_code_start); + + ShaderGLES2(); + +public: + enum { + CUSTOM_SHADER_DISABLED = 0 + }; + + GLint get_uniform_location(const String &p_name) const; + GLint get_uniform_location(int p_index) const; + + static _FORCE_INLINE_ ShaderGLES2 *get_active() { return active; } + bool bind(); + void unbind(); + void bind_uniforms(); + + inline GLuint get_program() const { return version ? version->id : 0; } + + void clear_caches(); + + _FORCE_INLINE_ void _set_uniform_value(GLint p_uniform, const Pair<ShaderLanguage::DataType, Vector<ShaderLanguage::ConstantNode::Value> > &value) { + if (p_uniform < 0) + return; + + const Vector<ShaderLanguage::ConstantNode::Value> &values = value.second; + + switch (value.first) { + case ShaderLanguage::TYPE_BOOL: { + glUniform1i(p_uniform, values[0].boolean); + } break; + + case ShaderLanguage::TYPE_BVEC2: { + glUniform2i(p_uniform, values[0].boolean, values[1].boolean); + } break; + + case ShaderLanguage::TYPE_BVEC3: { + glUniform3i(p_uniform, values[0].boolean, values[1].boolean, values[2].boolean); + } break; + + case ShaderLanguage::TYPE_BVEC4: { + glUniform4i(p_uniform, values[0].boolean, values[1].boolean, values[2].boolean, values[3].boolean); + } break; + + case ShaderLanguage::TYPE_INT: { + glUniform1i(p_uniform, values[0].sint); + } break; + + case ShaderLanguage::TYPE_IVEC2: { + glUniform2i(p_uniform, values[0].sint, values[1].sint); + } break; + + case ShaderLanguage::TYPE_IVEC3: { + glUniform3i(p_uniform, values[0].sint, values[1].sint, values[2].sint); + } break; + + case ShaderLanguage::TYPE_IVEC4: { + glUniform4i(p_uniform, values[0].sint, values[1].sint, values[2].sint, values[3].sint); + } break; + + case ShaderLanguage::TYPE_UINT: { + glUniform1i(p_uniform, values[0].uint); + } break; + + case ShaderLanguage::TYPE_UVEC2: { + glUniform2i(p_uniform, values[0].uint, values[1].uint); + } break; + + case ShaderLanguage::TYPE_UVEC3: { + glUniform3i(p_uniform, values[0].uint, values[1].uint, values[2].uint); + } break; + + case ShaderLanguage::TYPE_UVEC4: { + glUniform4i(p_uniform, values[0].uint, values[1].uint, values[2].uint, values[3].uint); + } break; + + case ShaderLanguage::TYPE_FLOAT: { + glUniform1f(p_uniform, values[0].real); + } break; + + case ShaderLanguage::TYPE_VEC2: { + glUniform2f(p_uniform, values[0].real, values[1].real); + } break; + + case ShaderLanguage::TYPE_VEC3: { + glUniform3f(p_uniform, values[0].real, values[1].real, values[2].real); + } break; + + case ShaderLanguage::TYPE_VEC4: { + glUniform4f(p_uniform, values[0].real, values[1].real, values[2].real, values[3].real); + } break; + + case ShaderLanguage::TYPE_MAT2: { + GLfloat mat[4]; + + for (int i = 0; i < 4; i++) { + mat[i] = values[i].real; + } + + glUniformMatrix2fv(p_uniform, 1, GL_FALSE, mat); + } break; + + case ShaderLanguage::TYPE_MAT3: { + GLfloat mat[9]; + + for (int i = 0; i < 9; i++) { + mat[i] = values[i].real; + } + + glUniformMatrix3fv(p_uniform, 1, GL_FALSE, mat); + + } break; + + case ShaderLanguage::TYPE_MAT4: { + GLfloat mat[16]; + + for (int i = 0; i < 16; i++) { + mat[i] = values[i].real; + } + + glUniformMatrix4fv(p_uniform, 1, GL_FALSE, mat); + + } break; + + case ShaderLanguage::TYPE_SAMPLER2D: { + + } break; + + case ShaderLanguage::TYPE_ISAMPLER2D: { + + } break; + + case ShaderLanguage::TYPE_USAMPLER2D: { + + } break; + + case ShaderLanguage::TYPE_SAMPLERCUBE: { + + } break; + } + } + _FORCE_INLINE_ void _set_uniform_variant(GLint p_uniform, const Variant &p_value) { if (p_uniform < 0) @@ -262,49 +430,13 @@ private: glUniformMatrix4fv(p_uniform, 1, false, matrix); } break; + case Variant::OBJECT: { + + } break; default: { ERR_FAIL(); } // do nothing } } - Map<uint32_t, Variant> uniform_defaults; - Map<uint32_t, CameraMatrix> uniform_cameras; - -protected: - _FORCE_INLINE_ int _get_uniform(int p_which) const; - _FORCE_INLINE_ void _set_conditional(int p_which, bool p_value); - - void setup(const char **p_conditional_defines, - int p_conditional_count, - const char **p_uniform_names, - int p_uniform_count, - const AttributePair *p_attribute_pairs, - int p_attribute_count, - const TexUnitPair *p_texunit_pairs, - int p_texunit_pair_count, - const char *p_vertex_code, - const char *p_fragment_code, - int p_vertex_code_start, - int p_fragment_code_start); - - ShaderGLES2(); - -public: - enum { - CUSTOM_SHADER_DISABLED = 0 - }; - - GLint get_uniform_location(const String &p_name) const; - GLint get_uniform_location(int p_index) const; - - static _FORCE_INLINE_ ShaderGLES2 *get_active() { return active; } - bool bind(); - void unbind(); - void bind_uniforms(); - - inline GLuint get_program() const { return version ? version->id : 0; } - - void clear_caches(); - uint32_t create_custom_shader(); void set_custom_shader_code(uint32_t p_code_id, const String &p_vertex, @@ -331,6 +463,10 @@ public: uniforms_dirty = true; } + // this void* is actually a RasterizerStorageGLES2::Material, but C++ doesn't + // like forward declared nested classes. + void use_material(void *p_material); + uint32_t get_version() const { return new_conditional_version.version; } void set_uniform_camera(int p_idx, const CameraMatrix &p_mat) { diff --git a/drivers/gles2/shaders/SCsub b/drivers/gles2/shaders/SCsub index 5de3e1ac90..acb93fff8f 100644 --- a/drivers/gles2/shaders/SCsub +++ b/drivers/gles2/shaders/SCsub @@ -8,8 +8,8 @@ if 'GLES2_GLSL' in env['BUILDERS']: env.GLES2_GLSL('canvas.glsl'); # env.GLES2_GLSL('canvas_shadow.glsl'); env.GLES2_GLSL('scene.glsl'); -# env.GLES2_GLSL('cubemap_filter.glsl'); -# env.GLES2_GLSL('cube_to_dp.glsl'); + env.GLES2_GLSL('cubemap_filter.glsl'); + env.GLES2_GLSL('cube_to_dp.glsl'); # env.GLES2_GLSL('blend_shape.glsl'); # env.GLES2_GLSL('screen_space_reflection.glsl'); # env.GLES2_GLSL('effect_blur.glsl'); diff --git a/drivers/gles2/shaders/canvas.glsl b/drivers/gles2/shaders/canvas.glsl index 11c6ab9b76..e97822f9bf 100644 --- a/drivers/gles2/shaders/canvas.glsl +++ b/drivers/gles2/shaders/canvas.glsl @@ -27,7 +27,7 @@ uniform vec4 src_rect; #endif -uniform bool blit_pass; +uniform highp float time; VERTEX_SHADER_GLOBALS @@ -96,20 +96,20 @@ precision mediump float; precision mediump int; #endif -uniform sampler2D color_texture; // texunit:0 +uniform sampler2D color_texture; // texunit:-1 uniform highp vec2 color_texpixel_size; -uniform mediump sampler2D normal_texture; // texunit:1 +uniform mediump sampler2D normal_texture; // texunit:-2 varying mediump vec2 uv_interp; varying mediump vec4 color_interp; -uniform bool blit_pass; +uniform highp float time; uniform vec4 final_modulate; #ifdef SCREEN_TEXTURE_USED -uniform sampler2D screen_texture; // texunit:2 +uniform sampler2D screen_texture; // texunit:-3 #endif @@ -127,6 +127,10 @@ void main() { vec4 color = color_interp; color *= texture2D(color_texture, uv_interp); + +#ifdef SCREEN_UV_USED + vec2 screen_uv = gl_FragCoord.xy * screen_pixel_size; +#endif { FRAGMENT_SHADER_CODE diff --git a/drivers/gles2/shaders/copy.glsl b/drivers/gles2/shaders/copy.glsl index a21da68525..feaeb2152b 100644 --- a/drivers/gles2/shaders/copy.glsl +++ b/drivers/gles2/shaders/copy.glsl @@ -9,11 +9,20 @@ precision mediump int; #endif attribute highp vec4 vertex_attrib; // attrib:0 + +#if defined(USE_CUBEMAP) || defined(USE_PANORAMA) +attribute vec3 cube_in; // attrib:4 +#else attribute vec2 uv_in; // attrib:4 +#endif + attribute vec2 uv2_in; // attrib:5 +#if defined(USE_CUBEMAP) || defined(USE_PANORAMA) +varying vec3 cube_interp; +#else varying vec2 uv_interp; - +#endif varying vec2 uv2_interp; #ifdef USE_COPY_SECTION @@ -22,7 +31,12 @@ uniform vec4 copy_section; void main() { +#if defined(USE_CUBEMAP) || defined(USE_PANORAMA) + cube_interp = cube_in; +#else uv_interp = uv_in; +#endif + uv2_interp = uv2_in; gl_Position = vertex_attrib; @@ -34,6 +48,8 @@ void main() { [fragment] +#define M_PI 3.14159265359 + #ifdef USE_GLES_OVER_GL #define mediump #define highp @@ -42,21 +58,59 @@ precision mediump float; precision mediump int; #endif - +#if defined(USE_CUBEMAP) || defined(USE_PANORAMA) +varying vec3 cube_interp; +#else varying vec2 uv_interp; +#endif + +#ifdef USE_CUBEMAP +uniform samplerCube source_cube; // texunit:0 +#else uniform sampler2D source; // texunit:0 +#endif varying vec2 uv2_interp; +#ifdef USE_MULTIPLIER +uniform float multiplier; +#endif + #ifdef USE_CUSTOM_ALPHA uniform float custom_alpha; #endif +#if defined(USE_PANORAMA) || defined(USE_ASYM_PANO) + +vec4 texturePanorama(sampler2D pano, vec3 normal) { + + vec2 st = vec2( + atan(normal.x, normal.z), + acos(normal.y) + ); + + if(st.x < 0.0) + st.x += M_PI*2.0; + + st/=vec2(M_PI*2.0,M_PI); + + return texture2D(pano,st); + +} + +#endif void main() { - //vec4 color = color_interp; +#ifdef USE_PANORAMA + + vec4 color = texturePanorama(source, normalize(cube_interp)); + +#elif defined(USE_CUBEMAP) + vec4 color = textureCube(source_cube, normalize(cube_interp)); +#else vec4 color = texture2D( source, uv_interp ); +#endif #ifdef USE_NO_ALPHA @@ -67,6 +121,9 @@ void main() { color.a=custom_alpha; #endif +#ifdef USE_MULTIPLIER + color.rgb *= multiplier; +#endif gl_FragColor = color; } diff --git a/drivers/gles2/shaders/cube_to_dp.glsl b/drivers/gles2/shaders/cube_to_dp.glsl index 5ffc78c0b9..0b3f53a870 100644 --- a/drivers/gles2/shaders/cube_to_dp.glsl +++ b/drivers/gles2/shaders/cube_to_dp.glsl @@ -1,10 +1,17 @@ [vertex] +#ifdef USE_GLES_OVER_GL +#define mediump +#define highp +#else +precision mediump float; +precision mediump int; +#endif -layout(location=0) in highp vec4 vertex_attrib; -layout(location=4) in vec2 uv_in; +attribute highp vec4 vertex_attrib; // attrib:0 +attribute vec2 uv_in; // attrib:4 -out vec2 uv_interp; +varying vec2 uv_interp; void main() { @@ -14,9 +21,16 @@ void main() { [fragment] +#ifdef USE_GLES_OVER_GL +#define mediump +#define highp +#else +precision mediump float; +precision mediump int; +#endif uniform highp samplerCube source_cube; //texunit:0 -in vec2 uv_interp; +varying vec2 uv_interp; uniform bool z_flip; uniform highp float z_far; @@ -49,7 +63,7 @@ void main() { } //normal = normalize(vec3( uv_interp * 2.0 - 1.0, 1.0 )); - float depth = texture(source_cube,normal).r; + float depth = textureCube(source_cube,normal).r; // absolute values for direction cosines, bigger value equals closer to basis axis vec3 unorm = abs(normal); diff --git a/drivers/gles2/shaders/cubemap_filter.glsl b/drivers/gles2/shaders/cubemap_filter.glsl index 485fbb6ee0..62ecd9471b 100644 --- a/drivers/gles2/shaders/cubemap_filter.glsl +++ b/drivers/gles2/shaders/cubemap_filter.glsl @@ -1,11 +1,17 @@ [vertex] +#ifdef USE_GLES_OVER_GL +#define mediump +#define highp +#else +precision mediump float; +precision mediump int; +#endif -layout(location=0) in highp vec2 vertex; - -layout(location=4) in highp vec2 uv; +attribute highp vec2 vertex; // attrib:0 +attribute highp vec2 uv; // attrib:4 -out highp vec2 uv_interp; +varying highp vec2 uv_interp; void main() { @@ -15,174 +21,47 @@ void main() { [fragment] +#extension GL_ARB_shader_texture_lod : require -precision highp float; -precision highp int; +#ifdef USE_GLES_OVER_GL +#define mediump +#define highp +#else +precision mediump float; +precision mediump int; +#endif #ifdef USE_SOURCE_PANORAMA uniform sampler2D source_panorama; //texunit:0 -#endif - -#ifdef USE_SOURCE_DUAL_PARABOLOID_ARRAY -uniform sampler2DArray source_dual_paraboloid_array; //texunit:0 -uniform int source_array_index; -#endif - -#if !defined(USE_SOURCE_DUAL_PARABOLOID_ARRAY) && !defined(USE_SOURCE_PANORAMA) +#else uniform samplerCube source_cube; //texunit:0 #endif uniform int face_id; uniform float roughness; -in highp vec2 uv_interp; - - -layout(location = 0) out vec4 frag_color; +varying highp vec2 uv_interp; +uniform sampler2D radical_inverse_vdc_cache; // texunit:1 #define M_PI 3.14159265359 - -vec3 texelCoordToVec(vec2 uv, int faceID) -{ - mat3 faceUvVectors[6]; -/* - // -x - faceUvVectors[1][0] = vec3(0.0, 0.0, 1.0); // u -> +z - faceUvVectors[1][1] = vec3(0.0, -1.0, 0.0); // v -> -y - faceUvVectors[1][2] = vec3(-1.0, 0.0, 0.0); // -x face - - // +x - faceUvVectors[0][0] = vec3(0.0, 0.0, -1.0); // u -> -z - faceUvVectors[0][1] = vec3(0.0, -1.0, 0.0); // v -> -y - faceUvVectors[0][2] = vec3(1.0, 0.0, 0.0); // +x face - - // -y - faceUvVectors[3][0] = vec3(1.0, 0.0, 0.0); // u -> +x - faceUvVectors[3][1] = vec3(0.0, 0.0, -1.0); // v -> -z - faceUvVectors[3][2] = vec3(0.0, -1.0, 0.0); // -y face - - // +y - faceUvVectors[2][0] = vec3(1.0, 0.0, 0.0); // u -> +x - faceUvVectors[2][1] = vec3(0.0, 0.0, 1.0); // v -> +z - faceUvVectors[2][2] = vec3(0.0, 1.0, 0.0); // +y face - - // -z - faceUvVectors[5][0] = vec3(-1.0, 0.0, 0.0); // u -> -x - faceUvVectors[5][1] = vec3(0.0, -1.0, 0.0); // v -> -y - faceUvVectors[5][2] = vec3(0.0, 0.0, -1.0); // -z face - - // +z - faceUvVectors[4][0] = vec3(1.0, 0.0, 0.0); // u -> +x - faceUvVectors[4][1] = vec3(0.0, -1.0, 0.0); // v -> -y - faceUvVectors[4][2] = vec3(0.0, 0.0, 1.0); // +z face -*/ - - // -x - faceUvVectors[0][0] = vec3(0.0, 0.0, 1.0); // u -> +z - faceUvVectors[0][1] = vec3(0.0, -1.0, 0.0); // v -> -y - faceUvVectors[0][2] = vec3(-1.0, 0.0, 0.0); // -x face - - // +x - faceUvVectors[1][0] = vec3(0.0, 0.0, -1.0); // u -> -z - faceUvVectors[1][1] = vec3(0.0, -1.0, 0.0); // v -> -y - faceUvVectors[1][2] = vec3(1.0, 0.0, 0.0); // +x face - - // -y - faceUvVectors[2][0] = vec3(1.0, 0.0, 0.0); // u -> +x - faceUvVectors[2][1] = vec3(0.0, 0.0, -1.0); // v -> -z - faceUvVectors[2][2] = vec3(0.0, -1.0, 0.0); // -y face - - // +y - faceUvVectors[3][0] = vec3(1.0, 0.0, 0.0); // u -> +x - faceUvVectors[3][1] = vec3(0.0, 0.0, 1.0); // v -> +z - faceUvVectors[3][2] = vec3(0.0, 1.0, 0.0); // +y face - - // -z - faceUvVectors[4][0] = vec3(-1.0, 0.0, 0.0); // u -> -x - faceUvVectors[4][1] = vec3(0.0, -1.0, 0.0); // v -> -y - faceUvVectors[4][2] = vec3(0.0, 0.0, -1.0); // -z face - - // +z - faceUvVectors[5][0] = vec3(1.0, 0.0, 0.0); // u -> +x - faceUvVectors[5][1] = vec3(0.0, -1.0, 0.0); // v -> -y - faceUvVectors[5][2] = vec3(0.0, 0.0, 1.0); // +z face - - // out = u * s_faceUv[0] + v * s_faceUv[1] + s_faceUv[2]. - vec3 result = (faceUvVectors[faceID][0] * uv.x) + (faceUvVectors[faceID][1] * uv.y) + faceUvVectors[faceID][2]; - return normalize(result); -} - -vec3 ImportanceSampleGGX(vec2 Xi, float Roughness, vec3 N) -{ - float a = Roughness * Roughness; // DISNEY'S ROUGHNESS [see Burley'12 siggraph] - - // Compute distribution direction - float Phi = 2.0 * M_PI * Xi.x; - float CosTheta = sqrt((1.0 - Xi.y) / (1.0 + (a*a - 1.0) * Xi.y)); - float SinTheta = sqrt(1.0 - CosTheta * CosTheta); - - // Convert to spherical direction - vec3 H; - H.x = SinTheta * cos(Phi); - H.y = SinTheta * sin(Phi); - H.z = CosTheta; - - vec3 UpVector = abs(N.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0); - vec3 TangentX = normalize(cross(UpVector, N)); - vec3 TangentY = cross(N, TangentX); - - // Tangent to world space - return TangentX * H.x + TangentY * H.y + N * H.z; -} - -// http://graphicrants.blogspot.com.au/2013/08/specular-brdf-reference.html -float GGX(float NdotV, float a) -{ - float k = a / 2.0; - return NdotV / (NdotV * (1.0 - k) + k); -} - -// http://graphicrants.blogspot.com.au/2013/08/specular-brdf-reference.html -float G_Smith(float a, float nDotV, float nDotL) -{ - return GGX(nDotL, a * a) * GGX(nDotV, a * a); -} - -float radicalInverse_VdC(uint bits) { - bits = (bits << 16u) | (bits >> 16u); - bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u); - bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u); - bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u); - bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u); - return float(bits) * 2.3283064365386963e-10; // / 0x100000000 -} - -vec2 Hammersley(uint i, uint N) { - return vec2(float(i)/float(N), radicalInverse_VdC(i)); -} - - - #ifdef LOW_QUALITY -#define SAMPLE_COUNT 64u +#define SAMPLE_COUNT 64 #else -#define SAMPLE_COUNT 512u +#define SAMPLE_COUNT 512 #endif -uniform bool z_flip; - #ifdef USE_SOURCE_PANORAMA -vec4 texturePanorama(vec3 normal,sampler2D pano ) { +vec4 texturePanorama(sampler2D pano, vec3 normal) { vec2 st = vec2( - atan(normal.x, normal.z), - acos(normal.y) + atan(normal.x, normal.z), + acos(normal.y) ); if(st.x < 0.0) @@ -190,105 +69,119 @@ vec4 texturePanorama(vec3 normal,sampler2D pano ) { st/=vec2(M_PI*2.0,M_PI); - return textureLod(pano,st,0.0); + return texture2DLod(pano,st,0.0); } #endif -#ifdef USE_SOURCE_DUAL_PARABOLOID_ARRAY - - -vec4 textureDualParaboloidArray(vec3 normal) { - - vec3 norm = normalize(normal); - norm.xy/=1.0+abs(norm.z); - norm.xy=norm.xy * vec2(0.5,0.25) + vec2(0.5,0.25); - if (norm.z<0.0) { - norm.y=0.5-norm.y+0.5; - } - return textureLod(source_dual_paraboloid_array, vec3(norm.xy, float(source_array_index) ), 0.0); - +vec3 texelCoordToVec(vec2 uv, int faceID) { + mat3 faceUvVectors[6]; + + // -x + faceUvVectors[0][0] = vec3(0.0, 0.0, 1.0); // u -> +z + faceUvVectors[0][1] = vec3(0.0, -1.0, 0.0); // v -> -y + faceUvVectors[0][2] = vec3(-1.0, 0.0, 0.0); // -x face + + // +x + faceUvVectors[1][0] = vec3(0.0, 0.0, -1.0); // u -> -z + faceUvVectors[1][1] = vec3(0.0, -1.0, 0.0); // v -> -y + faceUvVectors[1][2] = vec3(1.0, 0.0, 0.0); // +x face + + // -y + faceUvVectors[2][0] = vec3(1.0, 0.0, 0.0); // u -> +x + faceUvVectors[2][1] = vec3(0.0, 0.0, -1.0); // v -> -z + faceUvVectors[2][2] = vec3(0.0, -1.0, 0.0); // -y face + + // +y + faceUvVectors[3][0] = vec3(1.0, 0.0, 0.0); // u -> +x + faceUvVectors[3][1] = vec3(0.0, 0.0, 1.0); // v -> +z + faceUvVectors[3][2] = vec3(0.0, 1.0, 0.0); // +y face + + // -z + faceUvVectors[4][0] = vec3(-1.0, 0.0, 0.0); // u -> -x + faceUvVectors[4][1] = vec3(0.0, -1.0, 0.0); // v -> -y + faceUvVectors[4][2] = vec3(0.0, 0.0, -1.0); // -z face + + // +z + faceUvVectors[5][0] = vec3(1.0, 0.0, 0.0); // u -> +x + faceUvVectors[5][1] = vec3(0.0, -1.0, 0.0); // v -> -y + faceUvVectors[5][2] = vec3(0.0, 0.0, 1.0); // +z face + + // out = u * s_faceUv[0] + v * s_faceUv[1] + s_faceUv[2]. + vec3 result = (faceUvVectors[faceID][0] * uv.x) + (faceUvVectors[faceID][1] * uv.y) + faceUvVectors[faceID][2]; + return normalize(result); } -#endif - -void main() { - -#ifdef USE_DUAL_PARABOLOID - - vec3 N = vec3( uv_interp * 2.0 - 1.0, 0.0 ); - N.z = 0.5 - 0.5*((N.x * N.x) + (N.y * N.y)); - N = normalize(N); - - if (z_flip) { - N.y=-N.y; //y is flipped to improve blending between both sides - N.z=-N.z; - } - +vec3 ImportanceSampleGGX(vec2 Xi, float Roughness, vec3 N) { + float a = Roughness * Roughness; // DISNEY'S ROUGHNESS [see Burley'12 siggraph] -#else - vec2 uv = (uv_interp * 2.0) - 1.0; - vec3 N = texelCoordToVec(uv, face_id); -#endif - //vec4 color = color_interp; + // Compute distribution direction + float Phi = 2.0 * M_PI * Xi.x; + float CosTheta = sqrt((1.0 - Xi.y) / (1.0 + (a*a - 1.0) * Xi.y)); + float SinTheta = sqrt(1.0 - CosTheta * CosTheta); -#ifdef USE_DIRECT_WRITE + // Convert to spherical direction + vec3 H; + H.x = SinTheta * cos(Phi); + H.y = SinTheta * sin(Phi); + H.z = CosTheta; -#ifdef USE_SOURCE_PANORAMA + vec3 UpVector = abs(N.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0); + vec3 TangentX = normalize(cross(UpVector, N)); + vec3 TangentY = cross(N, TangentX); - frag_color=vec4(texturePanorama(N,source_panorama).rgb,1.0); -#endif + // Tangent to world space + return TangentX * H.x + TangentY * H.y + N * H.z; +} -#ifdef USE_SOURCE_DUAL_PARABOLOID_ARRAY +float radical_inverse_VdC(int i) { + return texture2D(radical_inverse_vdc_cache, vec2(float(i) / 512.0, 0.0)).x; +} - frag_color=vec4(textureDualParaboloidArray(N).rgb,1.0); -#endif +vec2 Hammersley(int i, int N) { + return vec2(float(i) / float(N), radical_inverse_VdC(i)); +} -#if !defined(USE_SOURCE_DUAL_PARABOLOID_ARRAY) && !defined(USE_SOURCE_PANORAMA) +uniform bool z_flip; - N.y=-N.y; - frag_color=vec4(texture(N,source_cube).rgb,1.0); -#endif +void main() { + vec3 color = vec3(0.0); + vec2 uv = (uv_interp * 2.0) - 1.0; + vec3 N = texelCoordToVec(uv, face_id); + vec4 sum = vec4(0.0); -#else + for (int sample_num = 0; sample_num < SAMPLE_COUNT; sample_num++) { - vec4 sum = vec4(0.0, 0.0, 0.0, 0.0); + vec2 xi = Hammersley(sample_num, SAMPLE_COUNT); - for(uint sampleNum = 0u; sampleNum < SAMPLE_COUNT; sampleNum++) { - vec2 xi = Hammersley(sampleNum, SAMPLE_COUNT); + vec3 H = ImportanceSampleGGX(xi, roughness, N); + vec3 V = N; + vec3 L = normalize(2.0 * dot(V, H) * H - V); - vec3 H = ImportanceSampleGGX( xi, roughness, N ); - vec3 V = N; - vec3 L = normalize(2.0 * dot( V, H ) * H - V); + float NdotL = clamp(dot(N, L), 0.0, 1.0); - float ndotl = clamp(dot(N, L),0.0,1.0); + if (NdotL > 0.0) { - if (ndotl>0.0) { #ifdef USE_SOURCE_PANORAMA - sum.rgb += texturePanorama(H,source_panorama).rgb *ndotl; + sum.rgb += texturePanorama(source_panorama, H).rgb * NdotL; +#else + H.y = -H.y; + sum.rgb += textureCubeLod(source_cube, H, 0.0).rgb * NdotL; #endif -#ifdef USE_SOURCE_DUAL_PARABOLOID_ARRAY - - sum.rgb += textureDualParaboloidArray(H).rgb *ndotl; -#endif + sum.a += NdotL; -#if !defined(USE_SOURCE_DUAL_PARABOLOID_ARRAY) && !defined(USE_SOURCE_PANORAMA) - H.y=-H.y; - sum.rgb += textureLod(source_cube, H, 0.0).rgb *ndotl; -#endif - sum.a += ndotl; } + } - sum /= sum.a; - frag_color = vec4(sum.rgb, 1.0); + sum /= sum.a; -#endif + gl_FragColor = vec4(sum.rgb, 1.0); } diff --git a/drivers/gles2/shaders/scene.glsl b/drivers/gles2/shaders/scene.glsl index 79b989be4a..b945e696b1 100644 --- a/drivers/gles2/shaders/scene.glsl +++ b/drivers/gles2/shaders/scene.glsl @@ -1,940 +1,393 @@ [vertex] -#define M_PI 3.14159265359 +#ifdef USE_GLES_OVER_GL +#define mediump +#define highp +#else +precision mediump float; +precision mediump int; +#endif -/* -from VisualServer: +#include "stdlib.glsl" -ARRAY_VERTEX=0, -ARRAY_NORMAL=1, -ARRAY_TANGENT=2, -ARRAY_COLOR=3, -ARRAY_TEX_UV=4, -ARRAY_TEX_UV2=5, -ARRAY_BONES=6, -ARRAY_WEIGHTS=7, -ARRAY_INDEX=8, -*/ -//hack to use uv if no uv present so it works with lightmap +// +// attributes +// -/* INPUT ATTRIBS */ +attribute highp vec4 vertex_attrib; // attrib:0 +attribute vec3 normal_attrib; // attrib:1 -layout(location=0) in highp vec4 vertex_attrib; -layout(location=1) in vec3 normal_attrib; -#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) || defined(LIGHT_USE_ANISOTROPY) -layout(location=2) in vec4 tangent_attrib; +#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) +attribute vec4 tangent_attrib; // attrib:2 #endif -#if defined(ENABLE_COLOR_INTERP) -layout(location=3) in vec4 color_attrib; +#ifdef ENABLE_COLOR_INTERP +attribute vec4 color_attrib; // attrib:3 #endif -#if defined(ENABLE_UV_INTERP) -layout(location=4) in vec2 uv_attrib; +#ifdef ENABLE_UV_INTERP +attribute vec2 uv_attrib; // attrib:4 #endif -#if defined(ENABLE_UV2_INTERP) -layout(location=5) in vec2 uv2_attrib; +#ifdef ENABLE_UV2_INTERP +attribute vec2 uv2_attrib; // attrib:5 #endif -uniform float normal_mult; - #ifdef USE_SKELETON -layout(location=6) in ivec4 bone_indices; // attrib:6 -layout(location=7) in vec4 bone_weights; // attrib:7 -#endif - -#ifdef USE_INSTANCING - -layout(location=8) in highp vec4 instance_xform0; -layout(location=9) in highp vec4 instance_xform1; -layout(location=10) in highp vec4 instance_xform2; -layout(location=11) in lowp vec4 instance_color; - -#if defined(ENABLE_INSTANCE_CUSTOM) -layout(location=12) in highp vec4 instance_custom_data; -#endif - -#endif - -layout(std140) uniform SceneData { //ubo:0 - - highp mat4 projection_matrix; - highp mat4 inv_projection_matrix; - highp mat4 camera_inverse_matrix; - highp mat4 camera_matrix; - - mediump vec4 ambient_light_color; - mediump vec4 bg_color; - - mediump vec4 fog_color_enabled; - mediump vec4 fog_sun_color_amount; - - mediump float ambient_energy; - mediump float bg_energy; - - mediump float z_offset; - mediump float z_slope_scale; - highp float shadow_dual_paraboloid_render_zfar; - highp float shadow_dual_paraboloid_render_side; - highp vec2 viewport_size; - highp vec2 screen_pixel_size; - highp vec2 shadow_atlas_pixel_size; - highp vec2 directional_shadow_pixel_size; +#ifdef USE_SKELETON_SOFTWARE - highp float time; - highp float z_far; - mediump float reflection_multiplier; - mediump float subsurface_scatter_width; - mediump float ambient_occlusion_affect_light; +attribute highp vec4 bone_transform_row_0; // attrib:9 +attribute highp vec4 bone_transform_row_1; // attrib:10 +attribute highp vec4 bone_transform_row_2; // attrib:11 - bool fog_depth_enabled; - highp float fog_depth_begin; - highp float fog_depth_curve; - bool fog_transmit_enabled; - highp float fog_transmit_curve; - bool fog_height_enabled; - highp float fog_height_min; - highp float fog_height_max; - highp float fog_height_curve; - -}; - -uniform highp mat4 world_transform; - - -#ifdef USE_LIGHT_DIRECTIONAL +#else -layout(std140) uniform DirectionalLightData { //ubo:3 +attribute vec4 bone_ids; // attrib:6 +attribute highp vec4 bone_weights; // attrib:7 - highp vec4 light_pos_inv_radius; - mediump vec4 light_direction_attenuation; - mediump vec4 light_color_energy; - mediump vec4 light_params; //cone attenuation, angle, specular, shadow enabled, - mediump vec4 light_clamp; - mediump vec4 shadow_color_contact; - highp mat4 shadow_matrix1; - highp mat4 shadow_matrix2; - highp mat4 shadow_matrix3; - highp mat4 shadow_matrix4; - mediump vec4 shadow_split_offsets; -}; +uniform highp sampler2D bone_transforms; // texunit:-1 +uniform ivec2 skeleton_texture_size; #endif -#ifdef USE_VERTEX_LIGHTING -//omni and spot - -struct LightData { - - highp vec4 light_pos_inv_radius; - mediump vec4 light_direction_attenuation; - mediump vec4 light_color_energy; - mediump vec4 light_params; //cone attenuation, angle, specular, shadow enabled, - mediump vec4 light_clamp; - mediump vec4 shadow_color_contact; - highp mat4 shadow_matrix; - -}; - - -layout(std140) uniform OmniLightData { //ubo:4 - - LightData omni_lights[MAX_LIGHT_DATA_STRUCTS]; -}; - -layout(std140) uniform SpotLightData { //ubo:5 - - LightData spot_lights[MAX_LIGHT_DATA_STRUCTS]; -}; - -#ifdef USE_FORWARD_LIGHTING - - -uniform int omni_light_indices[MAX_FORWARD_LIGHTS]; -uniform int omni_light_count; - -uniform int spot_light_indices[MAX_FORWARD_LIGHTS]; -uniform int spot_light_count; - #endif -out vec4 diffuse_light_interp; -out vec4 specular_light_interp; - -void light_compute(vec3 N, vec3 L,vec3 V, vec3 light_color, float roughness, inout vec3 diffuse, inout vec3 specular) { - - float dotNL = max(dot(N,L), 0.0 ); - diffuse += dotNL * light_color / M_PI; - - if (roughness > 0.0) { - - vec3 H = normalize(V + L); - float dotNH = max(dot(N,H), 0.0 ); - float intensity = pow( dotNH, (1.0-roughness) * 256.0); - specular += light_color * intensity; +#ifdef USE_INSTANCING - } -} +attribute highp vec4 instance_xform_row_0; // attrib:12 +attribute highp vec4 instance_xform_row_1; // attrib:13 +attribute highp vec4 instance_xform_row_2; // attrib:14 -void light_process_omni(int idx, vec3 vertex, vec3 eye_vec,vec3 normal, float roughness,inout vec3 diffuse, inout vec3 specular) { +attribute highp vec4 instance_color; // attrib:15 +attribute highp vec4 instance_custom_data; // attrib:8 - vec3 light_rel_vec = omni_lights[idx].light_pos_inv_radius.xyz-vertex; - float light_length = length( light_rel_vec ); - float normalized_distance = light_length*omni_lights[idx].light_pos_inv_radius.w; - vec3 light_attenuation = vec3(pow( max(1.0 - normalized_distance, 0.0), omni_lights[idx].light_direction_attenuation.w )); +#endif - light_compute(normal,normalize(light_rel_vec),eye_vec,omni_lights[idx].light_color_energy.rgb * light_attenuation,roughness,diffuse,specular); -} -void light_process_spot(int idx, vec3 vertex, vec3 eye_vec, vec3 normal, float roughness, inout vec3 diffuse, inout vec3 specular) { +// +// uniforms +// - vec3 light_rel_vec = spot_lights[idx].light_pos_inv_radius.xyz-vertex; - float light_length = length( light_rel_vec ); - float normalized_distance = light_length*spot_lights[idx].light_pos_inv_radius.w; - vec3 light_attenuation = vec3(pow( max(1.0 - normalized_distance, 0.001), spot_lights[idx].light_direction_attenuation.w )); - vec3 spot_dir = spot_lights[idx].light_direction_attenuation.xyz; - float spot_cutoff=spot_lights[idx].light_params.y; - float scos = max(dot(-normalize(light_rel_vec), spot_dir),spot_cutoff); - float spot_rim = (1.0 - scos) / (1.0 - spot_cutoff); - light_attenuation *= 1.0 - pow( max(spot_rim,0.001), spot_lights[idx].light_params.x); +uniform mat4 camera_matrix; +uniform mat4 camera_inverse_matrix; +uniform mat4 projection_matrix; +uniform mat4 projection_inverse_matrix; +uniform mat4 world_transform; - light_compute(normal,normalize(light_rel_vec),eye_vec,spot_lights[idx].light_color_energy.rgb*light_attenuation,roughness,diffuse,specular); -} +uniform highp float time; +uniform float normal_mult; +#ifdef RENDER_DEPTH +uniform float light_bias; +uniform float light_normal_bias; #endif -/* Varyings */ -out highp vec3 vertex_interp; -out vec3 normal_interp; +// +// varyings +// -#if defined(ENABLE_COLOR_INTERP) -out vec4 color_interp; -#endif +varying highp vec3 vertex_interp; +varying vec3 normal_interp; -#if defined(ENABLE_UV_INTERP) -out vec2 uv_interp; +#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) +varying vec3 tangent_interp; +varying vec3 binormal_interp; #endif -#if defined(ENABLE_UV2_INTERP) -out vec2 uv2_interp; +#ifdef ENABLE_COLOR_INTERP +varying vec4 color_interp; #endif - -#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) || defined(LIGHT_USE_ANISOTROPY) -out vec3 tangent_interp; -out vec3 binormal_interp; +#ifdef ENABLE_UV_INTERP +varying vec2 uv_interp; #endif - - - - -#if defined(USE_MATERIAL) - -layout(std140) uniform UniformData { //ubo:1 - -MATERIAL_UNIFORMS - -}; - -#endif - -VERTEX_SHADER_GLOBALS - -#ifdef RENDER_DEPTH_DUAL_PARABOLOID - -out highp float dp_clip; - -#endif - -#define SKELETON_TEXTURE_WIDTH 256 - -#ifdef USE_SKELETON -uniform highp sampler2D skeleton_texture; //texunit:-1 +#ifdef ENABLE_UV2_INTERP +varying vec2 uv2_interp; #endif -out highp vec4 position_interp; -// FIXME: This triggers a Mesa bug that breaks rendering, so disabled for now. -// See GH-13450 and https://bugs.freedesktop.org/show_bug.cgi?id=100316 -//invariant gl_Position; +VERTEX_SHADER_GLOBALS void main() { - highp vec4 vertex = vertex_attrib; // vec4(vertex_attrib.xyz * data_attrib.x,1.0); + highp vec4 vertex = vertex_attrib; mat4 world_matrix = world_transform; - #ifdef USE_INSTANCING - { - highp mat4 m=mat4(instance_xform0,instance_xform1,instance_xform2,vec4(0.0,0.0,0.0,1.0)); + highp mat4 m = mat4(instance_xform_row_0, + instance_xform_row_1, + instance_xform_row_2, + vec4(0.0, 0.0, 0.0, 1.0)); world_matrix = world_matrix * transpose(m); } #endif vec3 normal = normal_attrib * normal_mult; - -#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) || defined(LIGHT_USE_ANISOTROPY) +#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) vec3 tangent = tangent_attrib.xyz; - tangent*=normal_mult; + tangent *= normal_mult; float binormalf = tangent_attrib.a; + vec3 binormal = normalize(cross(normal, tangent) * binormalf); #endif -#if defined(ENABLE_COLOR_INTERP) +#ifdef ENABLE_COLOR_INTERP color_interp = color_attrib; -#if defined(USE_INSTANCING) +#ifdef USE_INSTANCING color_interp *= instance_color; #endif - #endif -#ifdef USE_SKELETON - { - //skeleton transform - ivec2 tex_ofs = ivec2( bone_indices.x%256, (bone_indices.x/256)*3 ); - highp mat3x4 m = mat3x4( - texelFetch(skeleton_texture,tex_ofs,0), - texelFetch(skeleton_texture,tex_ofs+ivec2(0,1),0), - texelFetch(skeleton_texture,tex_ofs+ivec2(0,2),0) - ) * bone_weights.x; +#ifdef ENABLE_UV_INTERP + uv_interp = uv_attrib; +#endif - tex_ofs = ivec2( bone_indices.y%256, (bone_indices.y/256)*3 ); +#ifdef ENABLE_UV2_INTERP + uv2_interp = uv2_attrib; +#endif - m+= mat3x4( - texelFetch(skeleton_texture,tex_ofs,0), - texelFetch(skeleton_texture,tex_ofs+ivec2(0,1),0), - texelFetch(skeleton_texture,tex_ofs+ivec2(0,2),0) - ) * bone_weights.y; +#if !defined(SKIP_TRANSFORM_USED) && defined(VERTEX_WORLD_COORDS_USED) + vertex = world_matrix * vertex; + normal = normalize((world_matrix * vec4(normal, 0.0)).xyz); +#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) - tex_ofs = ivec2( bone_indices.z%256, (bone_indices.z/256)*3 ); + tangent = normalize((world_matrix * vec4(tangent, 0.0)),xyz); + binormal = normalize((world_matrix * vec4(binormal, 0.0)).xyz); +#endif +#endif - m+= mat3x4( - texelFetch(skeleton_texture,tex_ofs,0), - texelFetch(skeleton_texture,tex_ofs+ivec2(0,1),0), - texelFetch(skeleton_texture,tex_ofs+ivec2(0,2),0) - ) * bone_weights.z; +#ifdef USE_SKELETON + highp mat4 bone_transform = mat4(0.0); - tex_ofs = ivec2( bone_indices.w%256, (bone_indices.w/256)*3 ); +#ifdef USE_SKELETON_SOFTWARE + // passing the transform as attributes - m+= mat3x4( - texelFetch(skeleton_texture,tex_ofs,0), - texelFetch(skeleton_texture,tex_ofs+ivec2(0,1),0), - texelFetch(skeleton_texture,tex_ofs+ivec2(0,2),0) - ) * bone_weights.w; + bone_transform[0] = vec4(bone_transform_row_0.x, bone_transform_row_1.x, bone_transform_row_2.x, 0.0); + bone_transform[1] = vec4(bone_transform_row_0.y, bone_transform_row_1.y, bone_transform_row_2.y, 0.0); + bone_transform[2] = vec4(bone_transform_row_0.z, bone_transform_row_1.z, bone_transform_row_2.z, 0.0); + bone_transform[3] = vec4(bone_transform_row_0.w, bone_transform_row_1.w, bone_transform_row_2.w, 1.0); +#else + // look up transform from the "pose texture" + { - vertex.xyz = vertex * m; + for (int i = 0; i < 4; i++) { + ivec2 tex_ofs = ivec2(int(bone_ids[i]) * 3, 0); - normal = vec4(normal,0.0) * m; -#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) || defined(LIGHT_USE_ANISOTROPY) - tangent.xyz = vec4(tangent.xyz,0.0) * m; -#endif + highp mat4 b = mat4(texel2DFetch(bone_transforms, skeleton_texture_size, tex_ofs + ivec2(0, 0)), + texel2DFetch(bone_transforms, skeleton_texture_size, tex_ofs + ivec2(1, 0)), + texel2DFetch(bone_transforms, skeleton_texture_size, tex_ofs + ivec2(2, 0)), + vec4(0.0, 0.0, 0.0, 1.0)); + + bone_transform += transpose(b) * bone_weights[i]; + } } -#endif - - -#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) || defined(LIGHT_USE_ANISOTROPY) - vec3 binormal = normalize( cross(normal,tangent) * binormalf ); #endif -#if defined(ENABLE_UV_INTERP) - uv_interp = uv_attrib; + world_matrix = bone_transform * world_matrix; #endif -#if defined(ENABLE_UV2_INTERP) - uv2_interp = uv2_attrib; -#endif -#if defined(USE_INSTANCING) && defined(ENABLE_INSTANCE_CUSTOM) +#ifdef USE_INSTANCING vec4 instance_custom = instance_custom_data; #else vec4 instance_custom = vec4(0.0); -#endif - highp mat4 modelview = camera_inverse_matrix * world_matrix; - highp mat4 local_projection = projection_matrix; - -//using world coordinates -#if !defined(SKIP_TRANSFORM_USED) && defined(VERTEX_WORLD_COORDS_USED) - - vertex = world_matrix * vertex; - normal = normalize((world_matrix * vec4(normal,0.0)).xyz); - -#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) || defined(LIGHT_USE_ANISOTROPY) - - tangent = normalize((world_matrix * vec4(tangent,0.0)).xyz); - binormal = normalize((world_matrix * vec4(binormal,0.0)).xyz); -#endif #endif - float roughness=0.0; -//defines that make writing custom shaders easier -#define projection_matrix local_projection + mat4 modelview = camera_matrix * world_matrix; + #define world_transform world_matrix + { VERTEX_SHADER_CODE } + vec4 outvec = vertex; - -//using local coordinates (default) + // use local coordinates #if !defined(SKIP_TRANSFORM_USED) && !defined(VERTEX_WORLD_COORDS_USED) - vertex = modelview * vertex; - normal = normalize((modelview * vec4(normal,0.0)).xyz); + normal = normalize((modelview * vec4(normal, 0.0)).xyz); -#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) || defined(LIGHT_USE_ANISOTROPY) - - tangent = normalize((modelview * vec4(tangent,0.0)).xyz); - binormal = normalize((modelview * vec4(binormal,0.0)).xyz); +#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) + tangent = normalize((modelview * vec4(tangent, 0.0)).xyz); + binormal = normalize((modelview * vec4(binormal, 0.0)).xyz); #endif #endif -//using world coordinates #if !defined(SKIP_TRANSFORM_USED) && defined(VERTEX_WORLD_COORDS_USED) - - vertex = camera_inverse_matrix * vertex; - normal = normalize((camera_inverse_matrix * vec4(normal,0.0)).xyz); - -#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) || defined(LIGHT_USE_ANISOTROPY) - - tangent = normalize((camera_inverse_matrix * vec4(tangent,0.0)).xyz); - binormal = normalize((camera_inverse_matrix * vec4(binormal,0.0)).xyz); + vertex = camera_matrix * vertex; + normal = normalize((camera_matrix * vec4(normal, 0.0)).xyz); +#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) + tangent = normalize((camera_matrix * vec4(tangent, 0.0)).xyz); + binormal = normalize((camera_matrix * vec4(binormal, 0.0)).xyz); #endif #endif vertex_interp = vertex.xyz; normal_interp = normal; - -#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) || defined(LIGHT_USE_ANISOTROPY) +#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) tangent_interp = tangent; binormal_interp = binormal; #endif - #ifdef RENDER_DEPTH + float z_ofs = light_bias; + z_ofs += (1.0 - abs(normal_interp.z)) * light_normal_bias; -#ifdef RENDER_DEPTH_DUAL_PARABOLOID - - vertex_interp.z*= shadow_dual_paraboloid_render_side; - normal_interp.z*= shadow_dual_paraboloid_render_side; - - dp_clip=vertex_interp.z; //this attempts to avoid noise caused by objects sent to the other parabolloid side due to bias - - //for dual paraboloid shadow mapping, this is the fastest but least correct way, as it curves straight edges - - highp vec3 vtx = vertex_interp+normalize(vertex_interp)*z_offset; - highp float distance = length(vtx); - vtx = normalize(vtx); - vtx.xy/=1.0-vtx.z; - vtx.z=(distance/shadow_dual_paraboloid_render_zfar); - vtx.z=vtx.z * 2.0 - 1.0; + vertex_interp.z -= z_ofs; - vertex.xyz=vtx; - vertex.w=1.0; - - -#else - - float z_ofs = z_offset; - z_ofs += (1.0-abs(normal_interp.z))*z_slope_scale; - vertex_interp.z-=z_ofs; - -#endif //RENDER_DEPTH_DUAL_PARABOLOID - -#endif //RENDER_DEPTH - - gl_Position = projection_matrix * vec4(vertex_interp,1.0); - - position_interp=gl_Position; - -#ifdef USE_VERTEX_LIGHTING - - diffuse_light_interp=vec4(0.0); - specular_light_interp=vec4(0.0); - -#ifdef USE_FORWARD_LIGHTING - - for(int i=0;i<omni_light_count;i++) { - light_process_omni(omni_light_indices[i],vertex_interp,-normalize( vertex_interp ),normal_interp,roughness,diffuse_light_interp.rgb,specular_light_interp.rgb); - } - - for(int i=0;i<spot_light_count;i++) { - light_process_spot(spot_light_indices[i],vertex_interp,-normalize( vertex_interp ),normal_interp,roughness,diffuse_light_interp.rgb,specular_light_interp.rgb); - } #endif -#ifdef USE_LIGHT_DIRECTIONAL - - vec3 directional_diffuse = vec3(0.0); - vec3 directional_specular = vec3(0.0); - light_compute(normal_interp,-light_direction_attenuation.xyz,-normalize( vertex_interp ),light_color_energy.rgb,roughness,directional_diffuse,directional_specular); - - float diff_avg = dot(diffuse_light_interp.rgb,vec3(0.33333)); - float diff_dir_avg = dot(directional_diffuse,vec3(0.33333)); - if (diff_avg>0.0) { - diffuse_light_interp.a=diff_dir_avg/(diff_avg+diff_dir_avg); - } else { - diffuse_light_interp.a=1.0; - } - - diffuse_light_interp.rgb+=directional_diffuse; - - float spec_avg = dot(specular_light_interp.rgb,vec3(0.33333)); - float spec_dir_avg = dot(directional_specular,vec3(0.33333)); - if (spec_avg>0.0) { - specular_light_interp.a=spec_dir_avg/(spec_avg+spec_dir_avg); - } else { - specular_light_interp.a=1.0; - } - - specular_light_interp.rgb+=directional_specular; - -#endif //USE_LIGHT_DIRECTIONAL - - -#endif // USE_VERTEX_LIGHTING + gl_Position = projection_matrix * vec4(vertex_interp, 1.0); } - [fragment] +#extension GL_ARB_shader_texture_lod : require -/* texture unit usage, N is max_texture_unity-N - -1-skeleton -2-radiance -3-reflection_atlas -4-directional_shadow -5-shadow_atlas -6-decal_atlas -7-screen -8-depth -9-probe1 -10-probe2 - -*/ - -uniform highp mat4 world_transform; - -#define M_PI 3.14159265359 - -/* Varyings */ - -#if defined(ENABLE_COLOR_INTERP) -in vec4 color_interp; -#endif - -#if defined(ENABLE_UV_INTERP) -in vec2 uv_interp; -#endif - -#if defined(ENABLE_UV2_INTERP) -in vec2 uv2_interp; -#endif - -#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) || defined(LIGHT_USE_ANISOTROPY) -in vec3 tangent_interp; -in vec3 binormal_interp; -#endif - -in highp vec3 vertex_interp; -in vec3 normal_interp; - - -/* PBR CHANNELS */ - -//used on forward mainly -uniform bool no_ambient_light; - - - -#ifdef USE_RADIANCE_MAP - - - -layout(std140) uniform Radiance { //ubo:2 - - mat4 radiance_inverse_xform; - float radiance_ambient_contribution; - -}; - -#define RADIANCE_MAX_LOD 5.0 - -#ifdef USE_RADIANCE_MAP_ARRAY - -uniform sampler2DArray radiance_map; //texunit:-2 - -vec3 textureDualParaboloid(sampler2DArray p_tex, vec3 p_vec,float p_roughness) { - - vec3 norm = normalize(p_vec); - norm.xy/=1.0+abs(norm.z); - norm.xy=norm.xy * vec2(0.5,0.25) + vec2(0.5,0.25); - - // we need to lie the derivatives (normg) and assume that DP side is always the same - // to get proper texture filtering - vec2 normg=norm.xy; - if (norm.z>0.0) { - norm.y=0.5-norm.y+0.5; - } - - // thanks to OpenGL spec using floor(layer + 0.5) for texture arrays, - // it's easy to have precision errors using fract() to interpolate layers - // as such, using fixed point to ensure it works. - - float index = p_roughness * RADIANCE_MAX_LOD; - int indexi = int(index * 256.0); - vec3 base = textureGrad(p_tex, vec3(norm.xy, float(indexi/256)),dFdx(normg),dFdy(normg)).xyz; - vec3 next = textureGrad(p_tex, vec3(norm.xy, float(indexi/256+1)),dFdx(normg),dFdy(normg)).xyz; - return mix(base,next,float(indexi%256)/256.0); -} - +#ifdef USE_GLES_OVER_GL +#define mediump +#define highp #else - -uniform sampler2D radiance_map; //texunit:-2 - -vec3 textureDualParaboloid(sampler2D p_tex, vec3 p_vec,float p_roughness) { - - vec3 norm = normalize(p_vec); - norm.xy/=1.0+abs(norm.z); - norm.xy=norm.xy * vec2(0.5,0.25) + vec2(0.5,0.25); - if (norm.z>0.0) { - norm.y=0.5-norm.y+0.5; - } - return textureLod(p_tex, norm.xy, p_roughness * RADIANCE_MAX_LOD).xyz; -} - +precision mediump float; +precision mediump int; #endif -#endif - -/* Material Uniforms */ - - - -#if defined(USE_MATERIAL) - -layout(std140) uniform UniformData { - -MATERIAL_UNIFORMS - -}; - -#endif - -FRAGMENT_SHADER_GLOBALS - -layout(std140) uniform SceneData { +#include "stdlib.glsl" - highp mat4 projection_matrix; - highp mat4 inv_projection_matrix; - highp mat4 camera_inverse_matrix; - highp mat4 camera_matrix; - - mediump vec4 ambient_light_color; - mediump vec4 bg_color; - - mediump vec4 fog_color_enabled; - mediump vec4 fog_sun_color_amount; - - mediump float ambient_energy; - mediump float bg_energy; - - mediump float z_offset; - mediump float z_slope_scale; - highp float shadow_dual_paraboloid_render_zfar; - highp float shadow_dual_paraboloid_render_side; - - highp vec2 viewport_size; - highp vec2 screen_pixel_size; - highp vec2 shadow_atlas_pixel_size; - highp vec2 directional_shadow_pixel_size; - - highp float time; - highp float z_far; - mediump float reflection_multiplier; - mediump float subsurface_scatter_width; - mediump float ambient_occlusion_affect_light; - - bool fog_depth_enabled; - highp float fog_depth_begin; - highp float fog_depth_curve; - bool fog_transmit_enabled; - highp float fog_transmit_curve; - bool fog_height_enabled; - highp float fog_height_min; - highp float fog_height_max; - highp float fog_height_curve; -}; - -//directional light data - -#ifdef USE_LIGHT_DIRECTIONAL - -layout(std140) uniform DirectionalLightData { - - highp vec4 light_pos_inv_radius; - mediump vec4 light_direction_attenuation; - mediump vec4 light_color_energy; - mediump vec4 light_params; //cone attenuation, angle, specular, shadow enabled, - mediump vec4 light_clamp; - mediump vec4 shadow_color_contact; - highp mat4 shadow_matrix1; - highp mat4 shadow_matrix2; - highp mat4 shadow_matrix3; - highp mat4 shadow_matrix4; - mediump vec4 shadow_split_offsets; -}; - - -uniform highp sampler2DShadow directional_shadow; //texunit:-4 - -#endif - -#ifdef USE_VERTEX_LIGHTING -in vec4 diffuse_light_interp; -in vec4 specular_light_interp; -#endif -//omni and spot - -struct LightData { - - highp vec4 light_pos_inv_radius; - mediump vec4 light_direction_attenuation; - mediump vec4 light_color_energy; - mediump vec4 light_params; //cone attenuation, angle, specular, shadow enabled, - mediump vec4 light_clamp; - mediump vec4 shadow_color_contact; - highp mat4 shadow_matrix; - -}; - - -layout(std140) uniform OmniLightData { //ubo:4 - - LightData omni_lights[MAX_LIGHT_DATA_STRUCTS]; -}; - -layout(std140) uniform SpotLightData { //ubo:5 - - LightData spot_lights[MAX_LIGHT_DATA_STRUCTS]; -}; - - -uniform highp sampler2DShadow shadow_atlas; //texunit:-5 - - -struct ReflectionData { - - mediump vec4 box_extents; - mediump vec4 box_offset; - mediump vec4 params; // intensity, 0, interior , boxproject - mediump vec4 ambient; //ambient color, energy - mediump vec4 atlas_clamp; - highp mat4 local_matrix; //up to here for spot and omni, rest is for directional - //notes: for ambientblend, use distance to edge to blend between already existing global environment -}; - -layout(std140) uniform ReflectionProbeData { //ubo:6 - - ReflectionData reflections[MAX_REFLECTION_DATA_STRUCTS]; -}; -uniform mediump sampler2D reflection_atlas; //texunit:-3 +#define M_PI 3.14159265359 +// +// uniforms +// -#ifdef USE_FORWARD_LIGHTING +uniform mat4 camera_matrix; +uniform mat4 camera_inverse_matrix; +uniform mat4 projection_matrix; +uniform mat4 projection_inverse_matrix; -uniform int omni_light_indices[MAX_FORWARD_LIGHTS]; -uniform int omni_light_count; +uniform mat4 world_transform; -uniform int spot_light_indices[MAX_FORWARD_LIGHTS]; -uniform int spot_light_count; +uniform highp float time; -uniform int reflection_indices[MAX_FORWARD_LIGHTS]; -uniform int reflection_count; +#ifdef SCREEN_UV_USED +uniform vec2 screen_pixel_size; #endif +uniform highp sampler2D depth_buffer; //texunit:-5 #if defined(SCREEN_TEXTURE_USED) - -uniform highp sampler2D screen_texture; //texunit:-7 - +uniform highp sampler2D screen_texture; //texunit:-6 #endif -#ifdef USE_MULTIPLE_RENDER_TARGETS +#ifdef USE_RADIANCE_MAP -layout(location=0) out vec4 diffuse_buffer; -layout(location=1) out vec4 specular_buffer; -layout(location=2) out vec4 normal_mr_buffer; -#if defined(ENABLE_SSS) -layout(location=3) out float sss_buffer; -#endif +#define RADIANCE_MAX_LOD 6.0 -#else +uniform samplerCube radiance_map; // texunit:-2 -layout(location=0) out vec4 frag_color; +uniform mat4 radiance_inverse_xform; #endif -in highp vec4 position_interp; -uniform highp sampler2D depth_buffer; //texunit:-8 - -#ifdef USE_CONTACT_SHADOWS +uniform float bg_energy; -float contact_shadow_compute(vec3 pos, vec3 dir, float max_distance) { +uniform float ambient_sky_contribution; +uniform vec4 ambient_color; +uniform float ambient_energy; - if (abs(dir.z)>0.99) - return 1.0; +#ifdef LIGHT_PASS - vec3 endpoint = pos+dir*max_distance; - vec4 source = position_interp; - vec4 dest = projection_matrix * vec4(endpoint, 1.0); +#define LIGHT_TYPE_DIRECTIONAL 0 +#define LIGHT_TYPE_OMNI 1 +#define LIGHT_TYPE_SPOT 2 - vec2 from_screen = (source.xy / source.w) * 0.5 + 0.5; - vec2 to_screen = (dest.xy / dest.w) * 0.5 + 0.5; +// general for all lights +uniform int light_type; - vec2 screen_rel = to_screen - from_screen; +uniform float light_energy; +uniform vec4 light_color; +uniform float light_specular; - if (length(screen_rel)<0.00001) - return 1.0; //too small, don't do anything +// directional +uniform vec3 light_direction; - /*float pixel_size; //approximate pixel size +// omni +uniform vec3 light_position; - if (screen_rel.x > screen_rel.y) { +uniform float light_range; +uniform vec4 light_attenuation; - pixel_size = abs((pos.x-endpoint.x)/(screen_rel.x/screen_pixel_size.x)); - } else { - pixel_size = abs((pos.y-endpoint.y)/(screen_rel.y/screen_pixel_size.y)); +// spot +uniform float light_spot_attenuation; +uniform float light_spot_range; +uniform float light_spot_angle; - }*/ - vec4 bias = projection_matrix * vec4(pos+vec3(0.0,0.0,0.04), 1.0); //todo un-harcode the 0.04 +// shadows +uniform highp sampler2D light_shadow_atlas; //texunit:-4 +uniform float light_has_shadow; +uniform mat4 light_shadow_matrix; +uniform vec4 light_clamp; - vec2 pixel_incr = normalize(screen_rel)*screen_pixel_size; +// directional shadow +uniform highp sampler2D light_directional_shadow; // texunit:-4 +uniform vec4 light_split_offsets; - float steps = length(screen_rel) / length(pixel_incr); - steps = min(2000.0,steps); //put a limit to avoid freezing in some strange situation - //steps=10.0; - - vec4 incr = (dest - source)/steps; - float ratio=0.0; - float ratio_incr = 1.0/steps; - - while(steps>0.0) { - source += incr*2.0; - bias+=incr*2.0; - - vec3 uv_depth = (source.xyz / source.w) * 0.5 + 0.5; - float depth = texture(depth_buffer,uv_depth.xy).r; - - if (depth < uv_depth.z) { - if (depth > (bias.z/bias.w) * 0.5 + 0.5) { - return min(pow(ratio,4.0),1.0); - } else { - return 1.0; - } - } - - - ratio+=ratio_incr; - steps-=1.0; - } - - return 1.0; -} - +uniform mat4 light_shadow_matrix1; +uniform mat4 light_shadow_matrix2; +uniform mat4 light_shadow_matrix3; +uniform mat4 light_shadow_matrix4; #endif -// This returns the G_GGX function divided by 2 cos_theta_m, where in practice cos_theta_m is either N.L or N.V. -// We're dividing this factor off because the overall term we'll end up looks like -// (see, for example, the first unnumbered equation in B. Burley, "Physically Based Shading at Disney", SIGGRAPH 2012): -// -// F(L.V) D(N.H) G(N.L) G(N.V) / (4 N.L N.V) -// -// We're basically regouping this as -// -// F(L.V) D(N.H) [G(N.L)/(2 N.L)] [G(N.V) / (2 N.V)] // -// and thus, this function implements the [G(N.m)/(2 N.m)] part with m = L or V. +// varyings // -// The contents of the D and G (G1) functions (GGX) are taken from -// E. Heitz, "Understanding the Masking-Shadowing Function in Microfacet-Based BRDFs", J. Comp. Graph. Tech. 3 (2) (2014). -// Eqns 71-72 and 85-86 (see also Eqns 43 and 80). - -float G_GGX_2cos(float cos_theta_m, float alpha) { - // Schlick's approximation - // C. Schlick, "An Inexpensive BRDF Model for Physically-based Rendering", Computer Graphics Forum. 13 (3): 233 (1994) - // Eq. (19), although see Heitz (2014) the about the problems with his derivation. - // It nevertheless approximates GGX well with k = alpha/2. - float k = 0.5*alpha; - return 0.5 / (cos_theta_m * (1.0 - k) + k); - - // float cos2 = cos_theta_m*cos_theta_m; - // float sin2 = (1.0-cos2); - // return 1.0 /( cos_theta_m + sqrt(cos2 + alpha*alpha*sin2) ); -} -float D_GGX(float cos_theta_m, float alpha) { - float alpha2 = alpha*alpha; - float d = 1.0 + (alpha2-1.0)*cos_theta_m*cos_theta_m; - return alpha2/(M_PI * d * d); -} +varying highp vec3 vertex_interp; +varying vec3 normal_interp; -float G_GGX_anisotropic_2cos(float cos_theta_m, float alpha_x, float alpha_y, float cos_phi, float sin_phi) { - float cos2 = cos_theta_m * cos_theta_m; - float sin2 = (1.0-cos2); - float s_x = alpha_x * cos_phi; - float s_y = alpha_y * sin_phi; - return 1.0 / (cos_theta_m + sqrt(cos2 + (s_x*s_x + s_y*s_y)*sin2 )); -} +#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) +varying vec3 tangent_interp; +varying vec3 binormal_interp; +#endif -float D_GGX_anisotropic(float cos_theta_m, float alpha_x, float alpha_y, float cos_phi, float sin_phi) { - float cos2 = cos_theta_m * cos_theta_m; - float sin2 = (1.0-cos2); - float r_x = cos_phi/alpha_x; - float r_y = sin_phi/alpha_y; - float d = cos2 + sin2*(r_x * r_x + r_y * r_y); - return 1.0 / (M_PI * alpha_x * alpha_y * d * d ); -} +#ifdef ENABLE_COLOR_INTERP +varying vec4 color_interp; +#endif +#ifdef ENABLE_UV_INTERP +varying vec2 uv_interp; +#endif -float SchlickFresnel(float u) -{ - float m = 1.0-u; - float m2 = m*m; - return m2*m2*m; // pow(m,5) -} +#ifdef ENABLE_UV2_INTERP +varying vec2 uv2_interp; +#endif -float GTR1(float NdotH, float a) -{ - if (a >= 1.0) return 1.0/M_PI; - float a2 = a*a; - float t = 1.0 + (a2-1.0)*NdotH*NdotH; - return (a2-1.0) / (M_PI*log(a2)*t); -} +varying vec3 view_interp; vec3 metallic_to_specular_color(float metallic, float specular, vec3 albedo) { float dielectric = (0.034 * 2.0) * specular; @@ -942,1172 +395,488 @@ vec3 metallic_to_specular_color(float metallic, float specular, vec3 albedo) { return mix(vec3(dielectric), albedo, metallic); // TODO: reference? } -void light_compute(vec3 N, vec3 L, vec3 V, vec3 B, vec3 T, vec3 light_color, vec3 attenuation, vec3 diffuse_color, vec3 transmission, float specular_blob_intensity, float roughness, float metallic, float rim, float rim_tint, float clearcoat, float clearcoat_gloss, float anisotropy, inout vec3 diffuse_light, inout vec3 specular_light) { - -#if defined(USE_LIGHT_SHADER_CODE) -//light is written by the light shader - - vec3 normal = N; - vec3 albedo = diffuse_color; - vec3 light = L; - vec3 view = V; - -LIGHT_SHADER_CODE +FRAGMENT_SHADER_GLOBALS -#else - float NdotL = dot(N,L); - float cNdotL = max(NdotL, 0.0); // clamped NdotL +#ifdef LIGHT_PASS +void light_compute(vec3 N, + vec3 L, + vec3 V, + vec3 B, + vec3 T, + vec3 light_color, + vec3 attenuation, + vec3 diffuse_color, + vec3 transmission, + float specular_blob_intensity, + float roughness, + float metallic, + float rim, + float rim_tint, + float clearcoat, + float clearcoat_gloss, + float anisotropy, + inout vec3 diffuse_light, + inout vec3 specular_light) { + + float NdotL = dot(N, L); + float cNdotL = max(NdotL, 0.0); float NdotV = dot(N, V); float cNdotV = max(NdotV, 0.0); - if (metallic < 1.0) { -#if defined(DIFFUSE_OREN_NAYAR) - vec3 diffuse_brdf_NL; -#else - float diffuse_brdf_NL; // BRDF times N.L for calculating diffuse radiance -#endif + { + // calculate diffuse reflection + // TODO hardcode Oren Nayar for now + float diffuse_brdf_NL; -#if defined(DIFFUSE_LAMBERT_WRAP) - //energy conserving lambert wrap shader diffuse_brdf_NL = max(0.0,(NdotL + roughness) / ((1.0 + roughness) * (1.0 + roughness))); + // diffuse_brdf_NL = cNdotL * (1.0 / M_PI); -#elif defined(DIFFUSE_OREN_NAYAR) - - { - // see http://mimosa-pudica.net/improved-oren-nayar.html - float LdotV = dot(L, V); - - - float s = LdotV - NdotL * NdotV; - float t = mix(1.0, max(NdotL, NdotV), step(0.0, s)); - - float sigma2 = roughness * roughness; // TODO: this needs checking - vec3 A = 1.0 + sigma2 * (- 0.5 / (sigma2 + 0.33) + 0.17*diffuse_color / (sigma2 + 0.13) ); - float B = 0.45 * sigma2 / (sigma2 + 0.09); - - diffuse_brdf_NL = cNdotL * (A + vec3(B) * s / t) * (1.0 / M_PI); - } - -#elif defined(DIFFUSE_TOON) - - diffuse_brdf_NL = smoothstep(-roughness,max(roughness,0.01),NdotL); - -#elif defined(DIFFUSE_BURLEY) - - { - - - vec3 H = normalize(V + L); - float cLdotH = max(0.0,dot(L, H)); - - float FD90 = 0.5 + 2.0 * cLdotH * cLdotH * roughness; - float FdV = 1.0 + (FD90 - 1.0) * SchlickFresnel(cNdotV); - float FdL = 1.0 + (FD90 - 1.0) * SchlickFresnel(cNdotL); - diffuse_brdf_NL = (1.0 / M_PI) * FdV * FdL * cNdotL; - /* - float energyBias = mix(roughness, 0.0, 0.5); - float energyFactor = mix(roughness, 1.0, 1.0 / 1.51); - float fd90 = energyBias + 2.0 * VoH * VoH * roughness; - float f0 = 1.0; - float lightScatter = f0 + (fd90 - f0) * pow(1.0 - cNdotL, 5.0); - float viewScatter = f0 + (fd90 - f0) * pow(1.0 - cNdotV, 5.0); - - diffuse_brdf_NL = lightScatter * viewScatter * energyFactor;*/ - } -#else - //lambert - diffuse_brdf_NL = cNdotL * (1.0 / M_PI); -#endif - -#if defined(TRANSMISSION_USED) - diffuse_light += light_color * diffuse_color * mix(vec3(diffuse_brdf_NL), vec3(M_PI), transmission) * attenuation; -#else diffuse_light += light_color * diffuse_color * diffuse_brdf_NL * attenuation; -#endif - - - -#if defined(LIGHT_USE_RIM) - float rim_light = pow(1.0-cNdotV, (1.0-roughness)*16.0); - diffuse_light += rim_light * rim * mix(vec3(1.0),diffuse_color,rim_tint) * light_color; -#endif } - - if (roughness > 0.0) { // FIXME: roughness == 0 should not disable specular light entirely - - - // D - -#if defined(SPECULAR_BLINN) - - vec3 H = normalize(V + L); - float cNdotH = max(dot(N,H), 0.0 ); - float intensity = pow( cNdotH, (1.0-roughness) * 256.0); - specular_light += light_color * intensity * specular_blob_intensity * attenuation; - -#elif defined(SPECULAR_PHONG) - - vec3 R = normalize(-reflect(L,N)); - float cRdotV = max(0.0,dot(R,V)); - float intensity = pow( cRdotV, (1.0-roughness) * 256.0); - specular_light += light_color * intensity * specular_blob_intensity * attenuation; - -#elif defined(SPECULAR_TOON) + { + // calculate specular reflection vec3 R = normalize(-reflect(L,N)); - float RdotV = dot(R,V); - float mid = 1.0-roughness; - mid*=mid; - float intensity = smoothstep(mid-roughness*0.5, mid+roughness*0.5, RdotV) * mid; - diffuse_light += light_color * intensity * specular_blob_intensity * attenuation; // write to diffuse_light, as in toon shading you generally want no reflection - -#elif defined(SPECULAR_DISABLED) - //none.. - -#elif defined(SPECULAR_SCHLICK_GGX) - // shlick+ggx as default - - vec3 H = normalize(V + L); - - float cNdotH = max(dot(N,H), 0.0); - float cLdotH = max(dot(L,H), 0.0); - -# if defined(LIGHT_USE_ANISOTROPY) - - float aspect = sqrt(1.0-anisotropy*0.9); - float rx = roughness/aspect; - float ry = roughness*aspect; - float ax = rx*rx; - float ay = ry*ry; - float XdotH = dot( T, H ); - float YdotH = dot( B, H ); - float D = D_GGX_anisotropic(cNdotH, ax, ay, XdotH, YdotH); - float G = G_GGX_anisotropic_2cos(cNdotL, ax, ay, XdotH, YdotH) * G_GGX_anisotropic_2cos(cNdotV, ax, ay, XdotH, YdotH); - -# else - float alpha = roughness * roughness; - float D = D_GGX(cNdotH, alpha); - float G = G_GGX_2cos(cNdotL, alpha) * G_GGX_2cos(cNdotV, alpha); -# endif - // F - float F0 = 1.0; // FIXME - float cLdotH5 = SchlickFresnel(cLdotH); - float F = mix(cLdotH5, 1.0, F0); - - float specular_brdf_NL = cNdotL * D * F * G; - - specular_light += specular_brdf_NL * light_color * specular_blob_intensity * attenuation; -#endif - -#if defined(LIGHT_USE_CLEARCOAT) - if (clearcoat_gloss > 0.0) { -# if !defined(SPECULAR_SCHLICK_GGX) && !defined(SPECULAR_BLINN) - vec3 H = normalize(V + L); -# endif -# if !defined(SPECULAR_SCHLICK_GGX) - float cNdotH = max(dot(N,H), 0.0); - float cLdotH = max(dot(L,H), 0.0); - float cLdotH5 = SchlickFresnel(cLdotH); -#endif - float Dr = GTR1(cNdotH, mix(.1, .001, clearcoat_gloss)); - float Fr = mix(.04, 1.0, cLdotH5); - float Gr = G_GGX_2cos(cNdotL, .25) * G_GGX_2cos(cNdotV, .25); + float cRdotV = max(dot(R, V), 0.0); + float blob_intensity = pow(cRdotV, (1.0 - roughness) * 256.0); + specular_light += light_color * attenuation * blob_intensity * specular_blob_intensity; - - float specular_brdf_NL = 0.25 * clearcoat * Gr * Fr * Dr * cNdotL; - - specular_light += specular_brdf_NL * light_color * specular_blob_intensity * attenuation; - } -#endif } - - -#endif //defined(USE_LIGHT_SHADER_CODE) } -float sample_shadow(highp sampler2DShadow shadow, vec2 shadow_pixel_size, vec2 pos, float depth, vec4 clamp_rect) { - -#ifdef SHADOW_MODE_PCF_13 - - float avg=textureProj(shadow,vec4(pos,depth,1.0)); - avg+=textureProj(shadow,vec4(pos+vec2(shadow_pixel_size.x,0.0),depth,1.0)); - avg+=textureProj(shadow,vec4(pos+vec2(-shadow_pixel_size.x,0.0),depth,1.0)); - avg+=textureProj(shadow,vec4(pos+vec2(0.0,shadow_pixel_size.y),depth,1.0)); - avg+=textureProj(shadow,vec4(pos+vec2(0.0,-shadow_pixel_size.y),depth,1.0)); - avg+=textureProj(shadow,vec4(pos+vec2(shadow_pixel_size.x,shadow_pixel_size.y),depth,1.0)); - avg+=textureProj(shadow,vec4(pos+vec2(-shadow_pixel_size.x,shadow_pixel_size.y),depth,1.0)); - avg+=textureProj(shadow,vec4(pos+vec2(shadow_pixel_size.x,-shadow_pixel_size.y),depth,1.0)); - avg+=textureProj(shadow,vec4(pos+vec2(-shadow_pixel_size.x,-shadow_pixel_size.y),depth,1.0)); - avg+=textureProj(shadow,vec4(pos+vec2(shadow_pixel_size.x*2.0,0.0),depth,1.0)); - avg+=textureProj(shadow,vec4(pos+vec2(-shadow_pixel_size.x*2.0,0.0),depth,1.0)); - avg+=textureProj(shadow,vec4(pos+vec2(0.0,shadow_pixel_size.y*2.0),depth,1.0)); - avg+=textureProj(shadow,vec4(pos+vec2(0.0,-shadow_pixel_size.y*2.0),depth,1.0)); - return avg*(1.0/13.0); -#elif defined(SHADOW_MODE_PCF_5) - float avg=textureProj(shadow,vec4(pos,depth,1.0)); - avg+=textureProj(shadow,vec4(pos+vec2(shadow_pixel_size.x,0.0),depth,1.0)); - avg+=textureProj(shadow,vec4(pos+vec2(-shadow_pixel_size.x,0.0),depth,1.0)); - avg+=textureProj(shadow,vec4(pos+vec2(0.0,shadow_pixel_size.y),depth,1.0)); - avg+=textureProj(shadow,vec4(pos+vec2(0.0,-shadow_pixel_size.y),depth,1.0)); - return avg*(1.0/5.0); - -#else +// shadows - return textureProj(shadow,vec4(pos,depth,1.0)); - -#endif - -} - -#ifdef RENDER_DEPTH_DUAL_PARABOLOID - -in highp float dp_clip; - -#endif - - - -#if 0 -//need to save texture depth for this - -vec3 light_transmittance(float translucency,vec3 light_vec, vec3 normal, vec3 pos, float distance) { - - float scale = 8.25 * (1.0 - translucency) / subsurface_scatter_width; - float d = scale * distance; - - /** - * Armed with the thickness, we can now calculate the color by means of the - * precalculated transmittance profile. - * (It can be precomputed into a texture, for maximum performance): - */ - float dd = -d * d; - vec3 profile = vec3(0.233, 0.455, 0.649) * exp(dd / 0.0064) + - vec3(0.1, 0.336, 0.344) * exp(dd / 0.0484) + - vec3(0.118, 0.198, 0.0) * exp(dd / 0.187) + - vec3(0.113, 0.007, 0.007) * exp(dd / 0.567) + - vec3(0.358, 0.004, 0.0) * exp(dd / 1.99) + - vec3(0.078, 0.0, 0.0) * exp(dd / 7.41); - - /** - * Using the profile, we finally approximate the transmitted lighting from - * the back of the object: - */ - return profile * clamp(0.3 + dot(light_vec, normal),0.0,1.0); -} -#endif - -void light_process_omni(int idx, vec3 vertex, vec3 eye_vec,vec3 normal,vec3 binormal, vec3 tangent, vec3 albedo, vec3 transmission, float roughness, float metallic, float rim, float rim_tint, float clearcoat, float clearcoat_gloss, float anisotropy, float p_blob_intensity, inout vec3 diffuse_light, inout vec3 specular_light) { - - vec3 light_rel_vec = omni_lights[idx].light_pos_inv_radius.xyz-vertex; - float light_length = length( light_rel_vec ); - float normalized_distance = light_length*omni_lights[idx].light_pos_inv_radius.w; - vec3 light_attenuation = vec3(pow( max(1.0 - normalized_distance, 0.0), omni_lights[idx].light_direction_attenuation.w )); - - if (omni_lights[idx].light_params.w>0.5) { - //there is a shadowmap - - highp vec3 splane=(omni_lights[idx].shadow_matrix * vec4(vertex,1.0)).xyz; - float shadow_len=length(splane); - splane=normalize(splane); - vec4 clamp_rect=omni_lights[idx].light_clamp; - - if (splane.z>=0.0) { - - splane.z+=1.0; - - clamp_rect.y+=clamp_rect.w; - - } else { - - splane.z=1.0 - splane.z; - - /* - if (clamp_rect.z<clamp_rect.w) { - clamp_rect.x+=clamp_rect.z; - } else { - clamp_rect.y+=clamp_rect.w; - } - */ - - } - - splane.xy/=splane.z; - splane.xy=splane.xy * 0.5 + 0.5; - splane.z = shadow_len * omni_lights[idx].light_pos_inv_radius.w; - - splane.xy = clamp_rect.xy+splane.xy*clamp_rect.zw; - float shadow = sample_shadow(shadow_atlas,shadow_atlas_pixel_size,splane.xy,splane.z,clamp_rect); - -#ifdef USE_CONTACT_SHADOWS - - if (shadow>0.01 && omni_lights[idx].shadow_color_contact.a>0.0) { - - float contact_shadow = contact_shadow_compute(vertex,normalize(light_rel_vec),min(light_length,omni_lights[idx].shadow_color_contact.a)); - shadow=min(shadow,contact_shadow); - - } -#endif - light_attenuation*=mix(omni_lights[idx].shadow_color_contact.rgb,vec3(1.0),shadow); - } - - light_compute(normal,normalize(light_rel_vec),eye_vec,binormal,tangent,omni_lights[idx].light_color_energy.rgb,light_attenuation,albedo,transmission,omni_lights[idx].light_params.z*p_blob_intensity,roughness,metallic,rim,rim_tint,clearcoat,clearcoat_gloss,anisotropy,diffuse_light,specular_light); - -} - -void light_process_spot(int idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 binormal, vec3 tangent,vec3 albedo, vec3 transmission,float roughness, float metallic, float rim, float rim_tint, float clearcoat, float clearcoat_gloss,float anisotropy,float p_blob_intensity, inout vec3 diffuse_light, inout vec3 specular_light) { - - vec3 light_rel_vec = spot_lights[idx].light_pos_inv_radius.xyz-vertex; - float light_length = length( light_rel_vec ); - float normalized_distance = light_length*spot_lights[idx].light_pos_inv_radius.w; - vec3 light_attenuation = vec3(pow( max(1.0 - normalized_distance, 0.001), spot_lights[idx].light_direction_attenuation.w )); - vec3 spot_dir = spot_lights[idx].light_direction_attenuation.xyz; - float spot_cutoff=spot_lights[idx].light_params.y; - float scos = max(dot(-normalize(light_rel_vec), spot_dir),spot_cutoff); - float spot_rim = (1.0 - scos) / (1.0 - spot_cutoff); - light_attenuation *= 1.0 - pow( max(spot_rim,0.001), spot_lights[idx].light_params.x); - - if (spot_lights[idx].light_params.w>0.5) { - //there is a shadowmap - highp vec4 splane=(spot_lights[idx].shadow_matrix * vec4(vertex,1.0)); - splane.xyz/=splane.w; - - float shadow = sample_shadow(shadow_atlas,shadow_atlas_pixel_size,splane.xy,splane.z,spot_lights[idx].light_clamp); - -#ifdef USE_CONTACT_SHADOWS - if (shadow>0.01 && spot_lights[idx].shadow_color_contact.a>0.0) { - - float contact_shadow = contact_shadow_compute(vertex,normalize(light_rel_vec),min(light_length,spot_lights[idx].shadow_color_contact.a)); - shadow=min(shadow,contact_shadow); - - } -#endif - light_attenuation*=mix(spot_lights[idx].shadow_color_contact.rgb,vec3(1.0),shadow); - } - - light_compute(normal,normalize(light_rel_vec),eye_vec,binormal,tangent,spot_lights[idx].light_color_energy.rgb,light_attenuation,albedo,transmission,spot_lights[idx].light_params.z*p_blob_intensity,roughness,metallic,rim,rim_tint,clearcoat,clearcoat_gloss,anisotropy,diffuse_light,specular_light); - -} - -void reflection_process(int idx, vec3 vertex, vec3 normal,vec3 binormal, vec3 tangent,float roughness,float anisotropy,vec3 ambient,vec3 skybox, inout highp vec4 reflection_accum,inout highp vec4 ambient_accum) { - - vec3 ref_vec = normalize(reflect(vertex,normal)); - vec3 local_pos = (reflections[idx].local_matrix * vec4(vertex,1.0)).xyz; - vec3 box_extents = reflections[idx].box_extents.xyz; - - if (any(greaterThan(abs(local_pos),box_extents))) { //out of the reflection box - return; - } - - vec3 inner_pos = abs(local_pos / box_extents); - float blend = max(inner_pos.x,max(inner_pos.y,inner_pos.z)); - //make blend more rounded - blend=mix(length(inner_pos),blend,blend); - blend*=blend; - blend=1.001-blend; - - if (reflections[idx].params.x>0.0){// compute reflection - - vec3 local_ref_vec = (reflections[idx].local_matrix * vec4(ref_vec,0.0)).xyz; - - if (reflections[idx].params.w > 0.5) { //box project - - vec3 nrdir = normalize(local_ref_vec); - vec3 rbmax = (box_extents - local_pos)/nrdir; - vec3 rbmin = (-box_extents - local_pos)/nrdir; - - - vec3 rbminmax = mix(rbmin,rbmax,greaterThan(nrdir,vec3(0.0,0.0,0.0))); - - float fa = min(min(rbminmax.x, rbminmax.y), rbminmax.z); - vec3 posonbox = local_pos + nrdir * fa; - local_ref_vec = posonbox - reflections[idx].box_offset.xyz; - } - - - vec4 clamp_rect=reflections[idx].atlas_clamp; - vec3 norm = normalize(local_ref_vec); - norm.xy/=1.0+abs(norm.z); - norm.xy=norm.xy * vec2(0.5,0.25) + vec2(0.5,0.25); - if (norm.z>0.0) { - norm.y=0.5-norm.y+0.5; - } - - vec2 atlas_uv = norm.xy * clamp_rect.zw + clamp_rect.xy; - atlas_uv = clamp(atlas_uv,clamp_rect.xy,clamp_rect.xy+clamp_rect.zw); - - highp vec4 reflection; - reflection.rgb = textureLod(reflection_atlas,atlas_uv,roughness*5.0).rgb; - - if (reflections[idx].params.z < 0.5) { - reflection.rgb = mix(skybox,reflection.rgb,blend); - } - reflection.rgb*=reflections[idx].params.x; - reflection.a = blend; - reflection.rgb*=reflection.a; - - reflection_accum+=reflection; - } - - if (reflections[idx].ambient.a>0.0) { //compute ambient using skybox - - - vec3 local_amb_vec = (reflections[idx].local_matrix * vec4(normal,0.0)).xyz; - - vec3 splane=normalize(local_amb_vec); - vec4 clamp_rect=reflections[idx].atlas_clamp; - - splane.z*=-1.0; - if (splane.z>=0.0) { - splane.z+=1.0; - clamp_rect.y+=clamp_rect.w; - } else { - splane.z=1.0 - splane.z; - splane.y=-splane.y; - } - - splane.xy/=splane.z; - splane.xy=splane.xy * 0.5 + 0.5; - - splane.xy = splane.xy * clamp_rect.zw + clamp_rect.xy; - splane.xy = clamp(splane.xy,clamp_rect.xy,clamp_rect.xy+clamp_rect.zw); - - highp vec4 ambient_out; - ambient_out.a=blend; - ambient_out.rgb = textureLod(reflection_atlas,splane.xy,5.0).rgb; - ambient_out.rgb=mix(reflections[idx].ambient.rgb,ambient_out.rgb,reflections[idx].ambient.a); - if (reflections[idx].params.z < 0.5) { - ambient_out.rgb = mix(ambient,ambient_out.rgb,blend); - } - - ambient_out.rgb *= ambient_out.a; - ambient_accum+=ambient_out; - } else { - - highp vec4 ambient_out; - ambient_out.a=blend; - ambient_out.rgb=reflections[idx].ambient.rgb; - if (reflections[idx].params.z < 0.5) { - ambient_out.rgb = mix(ambient,ambient_out.rgb,blend); - } - ambient_out.rgb *= ambient_out.a; - ambient_accum+=ambient_out; - - } -} - -#ifdef USE_GI_PROBES - -uniform mediump sampler3D gi_probe1; //texunit:-9 -uniform highp mat4 gi_probe_xform1; -uniform highp vec3 gi_probe_bounds1; -uniform highp vec3 gi_probe_cell_size1; -uniform highp float gi_probe_multiplier1; -uniform highp float gi_probe_bias1; -uniform highp float gi_probe_normal_bias1; -uniform bool gi_probe_blend_ambient1; - -uniform mediump sampler3D gi_probe2; //texunit:-10 -uniform highp mat4 gi_probe_xform2; -uniform highp vec3 gi_probe_bounds2; -uniform highp vec3 gi_probe_cell_size2; -uniform highp float gi_probe_multiplier2; -uniform highp float gi_probe_bias2; -uniform highp float gi_probe_normal_bias2; -uniform bool gi_probe2_enabled; -uniform bool gi_probe_blend_ambient2; - -vec3 voxel_cone_trace(mediump sampler3D probe, vec3 cell_size, vec3 pos, vec3 ambient, bool blend_ambient, vec3 direction, float tan_half_angle, float max_distance, float p_bias) { - - float dist = p_bias;//1.0; //dot(direction,mix(vec3(-1.0),vec3(1.0),greaterThan(direction,vec3(0.0))))*2.0; - float alpha=0.0; - vec3 color = vec3(0.0); - - while(dist < max_distance && alpha < 0.95) { - float diameter = max(1.0, 2.0 * tan_half_angle * dist); - vec4 scolor = textureLod(probe, (pos + dist * direction) * cell_size, log2(diameter) ); - float a = (1.0 - alpha); - color += scolor.rgb * a; - alpha += a * scolor.a; - dist += diameter * 0.5; - } - - if (blend_ambient) { - color.rgb = mix(ambient,color.rgb,min(1.0,alpha/0.95)); - } - - return color; -} - -void gi_probe_compute(mediump sampler3D probe, mat4 probe_xform, vec3 bounds,vec3 cell_size,vec3 pos, vec3 ambient, vec3 environment, bool blend_ambient,float multiplier, mat3 normal_mtx,vec3 ref_vec, float roughness,float p_bias,float p_normal_bias, inout vec4 out_spec, inout vec4 out_diff) { - - - - vec3 probe_pos = (probe_xform * vec4(pos,1.0)).xyz; - vec3 ref_pos = (probe_xform * vec4(pos+ref_vec,1.0)).xyz; - ref_vec = normalize(ref_pos - probe_pos); - - probe_pos+=(probe_xform * vec4(normal_mtx[2],0.0)).xyz*p_normal_bias; - -/* out_diff.rgb = voxel_cone_trace(probe,cell_size,probe_pos,normalize((probe_xform * vec4(ref_vec,0.0)).xyz),0.0 ,100.0); - out_diff.a = 1.0; - return;*/ - //out_diff = vec4(textureLod(probe,probe_pos*cell_size,3.0).rgb,1.0); - //return; - - //this causes corrupted pixels, i have no idea why.. - if (any(bvec2(any(lessThan(probe_pos,vec3(0.0))),any(greaterThan(probe_pos,bounds))))) { - return; - } - - //vec3 blendv = probe_pos/bounds * 2.0 - 1.0; - //float blend = 1.001-max(blendv.x,max(blendv.y,blendv.z)); - float blend=1.0; - - float max_distance = length(bounds); - - //radiance -#ifdef VCT_QUALITY_HIGH - -#define MAX_CONE_DIRS 6 - vec3 cone_dirs[MAX_CONE_DIRS] = vec3[] ( - vec3(0, 0, 1), - vec3(0.866025, 0, 0.5), - vec3(0.267617, 0.823639, 0.5), - vec3(-0.700629, 0.509037, 0.5), - vec3(-0.700629, -0.509037, 0.5), - vec3(0.267617, -0.823639, 0.5) - ); - - float cone_weights[MAX_CONE_DIRS] = float[](0.25, 0.15, 0.15, 0.15, 0.15, 0.15); - float cone_angle_tan = 0.577; - float min_ref_tan = 0.0; -#else - -#define MAX_CONE_DIRS 4 - - vec3 cone_dirs[MAX_CONE_DIRS] = vec3[] ( - vec3(0.707107, 0, 0.707107), - vec3(0, 0.707107, 0.707107), - vec3(-0.707107, 0, 0.707107), - vec3(0, -0.707107, 0.707107) - ); - - float cone_weights[MAX_CONE_DIRS] = float[](0.25, 0.25, 0.25, 0.25); - float cone_angle_tan = 0.98269; - max_distance*=0.5; - float min_ref_tan = 0.2; - -#endif - vec3 light=vec3(0.0); - for(int i=0;i<MAX_CONE_DIRS;i++) { - - vec3 dir = normalize( (probe_xform * vec4(pos + normal_mtx * cone_dirs[i],1.0)).xyz - probe_pos); - light+=cone_weights[i] * voxel_cone_trace(probe,cell_size,probe_pos,ambient,blend_ambient,dir,cone_angle_tan,max_distance,p_bias); - - } - - light*=multiplier; - - out_diff += vec4(light*blend,blend); - - //irradiance - - vec3 irr_light = voxel_cone_trace(probe,cell_size,probe_pos,environment,blend_ambient,ref_vec,max(min_ref_tan,tan(roughness * 0.5 * M_PI)) ,max_distance,p_bias); - - irr_light *= multiplier; - //irr_light=vec3(0.0); - - out_spec += vec4(irr_light*blend,blend); +float sample_shadow(highp sampler2D shadow, + vec2 shadow_pixel_size, + vec2 pos, + float depth, + vec4 clamp_rect) +{ + // vec4 depth_value = texture2D(shadow, pos); + // return depth_value.z; + return texture2DProj(shadow, vec4(pos, depth, 1.0)).r; + // return (depth_value.x + depth_value.y + depth_value.z + depth_value.w) / 4.0; } -void gi_probes_compute(vec3 pos, vec3 normal, float roughness, inout vec3 out_specular, inout vec3 out_ambient) { - - roughness = roughness * roughness; - - vec3 ref_vec = normalize(reflect(normalize(pos),normal)); - - //find arbitrary tangent and bitangent, then build a matrix - vec3 v0 = abs(normal.z) < 0.999 ? vec3(0, 0, 1) : vec3(0, 1, 0); - vec3 tangent = normalize(cross(v0, normal)); - vec3 bitangent = normalize(cross(tangent, normal)); - mat3 normal_mat = mat3(tangent,bitangent,normal); - - vec4 diff_accum = vec4(0.0); - vec4 spec_accum = vec4(0.0); - - vec3 ambient = out_ambient; - out_ambient = vec3(0.0); - - vec3 environment = out_specular; - - out_specular = vec3(0.0); - - gi_probe_compute(gi_probe1,gi_probe_xform1,gi_probe_bounds1,gi_probe_cell_size1,pos,ambient,environment,gi_probe_blend_ambient1,gi_probe_multiplier1,normal_mat,ref_vec,roughness,gi_probe_bias1,gi_probe_normal_bias1,spec_accum,diff_accum); - - if (gi_probe2_enabled) { - - gi_probe_compute(gi_probe2,gi_probe_xform2,gi_probe_bounds2,gi_probe_cell_size2,pos,ambient,environment,gi_probe_blend_ambient2,gi_probe_multiplier2,normal_mat,ref_vec,roughness,gi_probe_bias2,gi_probe_normal_bias2,spec_accum,diff_accum); - } - - if (diff_accum.a>0.0) { - diff_accum.rgb/=diff_accum.a; - } - - if (spec_accum.a>0.0) { - spec_accum.rgb/=spec_accum.a; - } - - out_specular+=spec_accum.rgb; - out_ambient+=diff_accum.rgb; - -} - #endif +void main() +{ - -void main() { - -#ifdef RENDER_DEPTH_DUAL_PARABOLOID - - if (dp_clip>0.0) - discard; -#endif - - //lay out everything, whathever is unused is optimized away anyway highp vec3 vertex = vertex_interp; - vec3 albedo = vec3(0.8,0.8,0.8); + vec3 albedo = vec3(1.0); vec3 transmission = vec3(0.0); float metallic = 0.0; float specular = 0.5; - vec3 emission = vec3(0.0,0.0,0.0); + vec3 emission = vec3(0.0); float roughness = 1.0; float rim = 0.0; float rim_tint = 0.0; - float clearcoat=0.0; - float clearcoat_gloss=0.0; - float anisotropy = 1.0; - vec2 anisotropy_flow = vec2(1.0,0.0); - -#if defined(ENABLE_AO) - float ao=1.0; - float ao_light_affect=0.0; -#endif + float clearcoat = 0.0; + float clearcoat_gloss = 0.0; + float anisotropy = 0.0; + vec2 anisotropy_flow = vec2(1.0, 0.0); float alpha = 1.0; + float side = 1.0; -#ifdef METERIAL_DOUBLESIDED - float side=float(gl_FrontFacing)*2.0-1.0; -#else - float side=1.0; +#if defined(ENABLE_AO) + float ao = 1.0; + float ao_light_affect = 0.0; #endif -#if defined(ALPHA_SCISSOR_USED) - float alpha_scissor = 0.5; -#endif - -#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) || defined(LIGHT_USE_ANISOTROPY) - vec3 binormal = normalize(binormal_interp)*side; - vec3 tangent = normalize(tangent_interp)*side; +#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) + vec3 binormal = normalize(binormal_interp) * side; + vec3 tangent = normalize(tangent_interp) * side; #else vec3 binormal = vec3(0.0); vec3 tangent = vec3(0.0); #endif - vec3 normal = normalize(normal_interp)*side; - -#if defined(ENABLE_UV_INTERP) - vec2 uv = uv_interp; -#endif - -#if defined(ENABLE_UV2_INTERP) - vec2 uv2 = uv2_interp; -#endif - -#if defined(ENABLE_COLOR_INTERP) - vec4 color = color_interp; -#endif + vec3 normal = normalize(normal_interp) * side; #if defined(ENABLE_NORMALMAP) - - vec3 normalmap = vec3(0.0); + vec3 normalmap = vec3(0.5); #endif + float normaldepth = 1.0; - float normaldepth=1.0; -#if defined(SCREEN_UV_USED) - vec2 screen_uv = gl_FragCoord.xy*screen_pixel_size; +#ifdef ALPHA_SCISSOR_USED + float alpha_scissor = 0.5; #endif -#if defined (ENABLE_SSS) - float sss_strength=0.0; +#ifdef SCREEN_UV_USED + vec2 screen_uv = gl_FragCoord.xy * screen_pixel_size; #endif { - FRAGMENT_SHADER_CODE -} +} -#if defined(ALPHA_SCISSOR_USED) - if (alpha<alpha_scissor) { - discard; - } -#endif - -#ifdef USE_OPAQUE_PREPASS +#if defined(ENABLE_NORMALMAP) + normalmap.xy = normalmap.xy * 2.0 - 1.0; + normalmap.z = sqrt(max(0.0, 1.0 - dot(normalmap.xy, normalmap.xy))); - if (alpha<0.99) { - discard; - } + // normal = normalize(mix(normal_interp, tangent * normalmap.x + binormal * normalmap.y + normal * normalmap.z, normaldepth)) * side; + normal = normalmap; #endif -#if defined(ENABLE_NORMALMAP) + normal = normalize(normal); - normalmap.xy=normalmap.xy*2.0-1.0; - normalmap.z=sqrt(1.0-dot(normalmap.xy,normalmap.xy)); //always ignore Z, as it can be RG packed, Z may be pos/neg, etc. + vec3 N = normal; - normal = normalize( mix(normal_interp,tangent * normalmap.x + binormal * normalmap.y + normal * normalmap.z,normaldepth) ) * side; + vec3 specular_light = vec3(0.0, 0.0, 0.0); + vec3 diffuse_light = vec3(0.0, 0.0, 0.0); -#endif + vec3 ambient_light = vec3(0.0, 0.0, 0.0); -#if defined(LIGHT_USE_ANISOTROPY) - - if (anisotropy>0.01) { - //rotation matrix - mat3 rot = mat3( tangent, binormal, normal ); - //make local to space - tangent = normalize(rot * vec3(anisotropy_flow.x,anisotropy_flow.y,0.0)); - binormal = normalize(rot * vec3(-anisotropy_flow.y,anisotropy_flow.x,0.0)); - } + vec3 env_reflection_light = vec3(0.0, 0.0, 0.0); -#endif + vec3 eye_position = -normalize(vertex_interp); -#ifdef ENABLE_CLIP_ALPHA - if (albedo.a<0.99) { - //used for doublepass and shadowmapping +#ifdef ALPHA_SCISSOR_USED + if (alpha < alpha_scissor) { discard; } #endif -/////////////////////// LIGHTING ////////////////////////////// - - //apply energy conservation - -#ifdef USE_VERTEX_LIGHTING - - vec3 specular_light = specular_light_interp.rgb; - vec3 diffuse_light = diffuse_light_interp.rgb; -#else - - vec3 specular_light = vec3(0.0,0.0,0.0); - vec3 diffuse_light = vec3(0.0,0.0,0.0); - -#endif +// +// Lighting +// +#ifdef LIGHT_PASS - vec3 ambient_light; - vec3 env_reflection_light = vec3(0.0,0.0,0.0); + if (light_type == LIGHT_TYPE_OMNI) { + vec3 light_vec = light_position - vertex; + float light_length = length(light_vec); - vec3 eye_vec = -normalize( vertex_interp ); + float normalized_distance = light_length / light_range; + float omni_attenuation = pow(1.0 - normalized_distance, light_attenuation.w); + vec3 attenuation = vec3(omni_attenuation); -#ifdef USE_RADIANCE_MAP + if (light_has_shadow > 0.5) { + highp vec3 splane = (light_shadow_matrix * vec4(vertex, 1.0)).xyz; + float shadow_len = length(splane); - if (no_ambient_light) { - ambient_light=vec3(0.0,0.0,0.0); - } else { - { + splane = normalize(splane); - { //read radiance from dual paraboloid + vec4 clamp_rect = light_clamp; - vec3 ref_vec = reflect(-eye_vec,normal); //2.0 * ndotv * normal - view; // reflect(v, n); - ref_vec=normalize((radiance_inverse_xform * vec4(ref_vec,0.0)).xyz); - vec3 radiance = textureDualParaboloid(radiance_map,ref_vec,roughness) * bg_energy; - env_reflection_light = radiance; + if (splane.z >= 0.0) { + splane.z += 1.0; + clamp_rect.y += clamp_rect.w; + } else { + splane.z = 1.0 - splane.z; } - //no longer a cubemap - //vec3 radiance = textureLod(radiance_cube, r, lod).xyz * ( brdf.x + brdf.y); - } + splane.xy /= splane.z; + splane.xy = splane.xy * 0.5 + 0.5; + splane.z = shadow_len / light_range; - { + splane.xy = clamp_rect.xy + splane.xy * clamp_rect.zw; - vec3 ambient_dir=normalize((radiance_inverse_xform * vec4(normal,0.0)).xyz); - vec3 env_ambient=textureDualParaboloid(radiance_map,ambient_dir,1.0) * bg_energy; + float shadow = sample_shadow(light_shadow_atlas, vec2(0.0), splane.xy, splane.z, clamp_rect); - ambient_light=mix(ambient_light_color.rgb,env_ambient,radiance_ambient_contribution); - //ambient_light=vec3(0.0,0.0,0.0); + if (shadow > splane.z) { + } else { + attenuation = vec3(0.0); + } } - } - -#else - - if (no_ambient_light){ - ambient_light=vec3(0.0,0.0,0.0); - } else { - ambient_light=ambient_light_color.rgb; - } -#endif - ambient_light*=ambient_energy; - - float specular_blob_intensity=1.0; -#if defined(SPECULAR_TOON) - specular_blob_intensity*=specular * 2.0; -#endif - -#if defined(USE_LIGHT_DIRECTIONAL) - - vec3 light_attenuation=vec3(1.0); - - float depth_z = -vertex.z; -#ifdef LIGHT_DIRECTIONAL_SHADOW + light_compute(normal, + normalize(light_vec), + eye_position, + binormal, + tangent, + light_color.xyz * light_energy, + attenuation, + albedo, + transmission, + specular * light_specular, + roughness, + metallic, + rim, + rim_tint, + clearcoat, + clearcoat_gloss, + anisotropy, + diffuse_light, + specular_light); + + } else if (light_type == LIGHT_TYPE_DIRECTIONAL) { + + vec3 light_vec = -light_direction; + vec3 attenuation = vec3(1.0, 1.0, 1.0); + + float depth_z = -vertex.z; + + if (light_has_shadow > 0.5) { #ifdef LIGHT_USE_PSSM4 - if (depth_z < shadow_split_offsets.w) { + if (depth_z < light_split_offsets.w) { #elif defined(LIGHT_USE_PSSM2) - if (depth_z < shadow_split_offsets.y) { + if (depth_z < light_split_offsets.y) { #else - if (depth_z < shadow_split_offsets.x) { -#endif //LIGHT_USE_PSSM4 + if (depth_z < light_split_offsets.x) { +#endif - vec3 pssm_coord; - float pssm_fade=0.0; + vec3 pssm_coord; + float pssm_fade = 0.0; #ifdef LIGHT_USE_PSSM_BLEND - float pssm_blend; - vec3 pssm_coord2; - bool use_blend=true; + float pssm_blend; + vec3 pssm_coord2; + bool use_blend = true; #endif - #ifdef LIGHT_USE_PSSM4 + if (depth_z < light_split_offsets.y) { + if (depth_z < light_split_offsets.x) { + highp vec4 splane = (light_shadow_matrix1 * vec4(vertex, 1.0)); + pssm_coord = splane.xyz / splane.w; +#ifdef LIGHT_USE_PSSM_BLEND + splane = (light_shadow_matrix2 * vec4(vertex, 1.0)); + pssm_coord2 = splane.xyz / splane.w; - if (depth_z < shadow_split_offsets.y) { - - if (depth_z < shadow_split_offsets.x) { - - highp vec4 splane=(shadow_matrix1 * vec4(vertex,1.0)); - pssm_coord=splane.xyz/splane.w; - - -#if defined(LIGHT_USE_PSSM_BLEND) - - splane=(shadow_matrix2 * vec4(vertex,1.0)); - pssm_coord2=splane.xyz/splane.w; - pssm_blend=smoothstep(0.0,shadow_split_offsets.x,depth_z); + pssm_blend = smoothstep(0.0, light_split_offsets.x, depth_z); #endif + } else { + highp vec4 splane = (light_shadow_matrix2 * vec4(vertex, 1.0)); + pssm_coord = splane.xyz / splane.w; - } else { - - highp vec4 splane=(shadow_matrix2 * vec4(vertex,1.0)); - pssm_coord=splane.xyz/splane.w; +#ifdef LIGHT_USE_PSSM_BLEND + splane = (light_shadow_matrix3 * vec4(vertex, 1.0)); + pssm_coord2 = splane.xyz / splane.w; -#if defined(LIGHT_USE_PSSM_BLEND) - splane=(shadow_matrix3 * vec4(vertex,1.0)); - pssm_coord2=splane.xyz/splane.w; - pssm_blend=smoothstep(shadow_split_offsets.x,shadow_split_offsets.y,depth_z); + pssm_blend = smoothstep(light_split_offsets.x, light_split_offsets.y, depth_z); #endif + } + } else { + if (depth_z < light_split_offsets.z) { - } - } else { - - - if (depth_z < shadow_split_offsets.z) { - - highp vec4 splane=(shadow_matrix3 * vec4(vertex,1.0)); - pssm_coord=splane.xyz/splane.w; + highp vec4 splane = (light_shadow_matrix3 * vec4(vertex, 1.0)); + pssm_coord = splane.xyz / splane.w; #if defined(LIGHT_USE_PSSM_BLEND) - splane=(shadow_matrix4 * vec4(vertex,1.0)); - pssm_coord2=splane.xyz/splane.w; - pssm_blend=smoothstep(shadow_split_offsets.y,shadow_split_offsets.z,depth_z); + splane = (light_shadow_matrix4 * vec4(vertex, 1.0)); + pssm_coord2 = splane.xyz / splane.w; + pssm_blend = smoothstep(light_split_offsets.y, light_split_offsets.z, depth_z); #endif - } else { + } else { - highp vec4 splane=(shadow_matrix4 * vec4(vertex,1.0)); - pssm_coord=splane.xyz/splane.w; - pssm_fade = smoothstep(shadow_split_offsets.z,shadow_split_offsets.w,depth_z); + highp vec4 splane = (light_shadow_matrix4 * vec4(vertex, 1.0)); + pssm_coord = splane.xyz / splane.w; + pssm_fade = smoothstep(light_split_offsets.z, light_split_offsets.w, depth_z); #if defined(LIGHT_USE_PSSM_BLEND) - use_blend=false; - + use_blend = false; #endif + } + } - } - } - - - -#endif //LIGHT_USE_PSSM4 +#endif // LIGHT_USE_PSSM4 #ifdef LIGHT_USE_PSSM2 + if (depth_z < light_split_offsets.x) { - if (depth_z < shadow_split_offsets.x) { - - highp vec4 splane=(shadow_matrix1 * vec4(vertex,1.0)); - pssm_coord=splane.xyz/splane.w; - + highp vec4 splane = (light_shadow_matrix1 * vec4(vertex, 1.0)); + pssm_coord = splane.xyz / splane.w; -#if defined(LIGHT_USE_PSSM_BLEND) - - splane=(shadow_matrix2 * vec4(vertex,1.0)); - pssm_coord2=splane.xyz/splane.w; - pssm_blend=smoothstep(0.0,shadow_split_offsets.x,depth_z); +#ifdef LIGHT_USE_PSSM_BLEND + splane = (light_shadow_matrix2 * vec4(vertex, 1.0)); + pssm_coord2 = splane.xyz / splane.w; + pssm_blend = smoothstep(0.0, light_split_offsets.x, depth_z); #endif - - } else { - highp vec4 splane=(shadow_matrix2 * vec4(vertex,1.0)); - pssm_coord=splane.xyz/splane.w; - pssm_fade = smoothstep(shadow_split_offsets.x,shadow_split_offsets.y,depth_z); -#if defined(LIGHT_USE_PSSM_BLEND) - use_blend=false; - + } else { + highp vec4 splane = (light_shadow_matrix2 * vec4(vertex, 1.0)); + pssm_coord = splane.xyz / splane.w; + pssm_fade = smoothstep(light_split_offsets.x, light_split_offsets.y, depth_z); +#ifdef LIGHT_USE_PSSM_BLEND + use_blend = false; #endif + } - } - -#endif //LIGHT_USE_PSSM2 +#endif // LIGHT_USE_PSSM2 #if !defined(LIGHT_USE_PSSM4) && !defined(LIGHT_USE_PSSM2) - { //regular orthogonal - highp vec4 splane=(shadow_matrix1 * vec4(vertex,1.0)); - pssm_coord=splane.xyz/splane.w; - } + { + highp vec4 splane = (light_shadow_matrix1 * vec4(vertex, 1.0)); + pssm_coord = splane.xyz / splane.w; + } #endif + float shadow = sample_shadow(light_shadow_atlas, vec2(0.0), pssm_coord.xy, pssm_coord.z, light_clamp); - //one one sample - - float shadow = sample_shadow(directional_shadow,directional_shadow_pixel_size,pssm_coord.xy,pssm_coord.z,light_clamp); - -#if defined(LIGHT_USE_PSSM_BLEND) - - if (use_blend) { - shadow=mix(shadow, sample_shadow(directional_shadow,directional_shadow_pixel_size,pssm_coord2.xy,pssm_coord2.z,light_clamp),pssm_blend); - } +#ifdef LIGHT_USE_PSSM_BLEND + if (use_blend) { + shadow = mix(shadow, sample_shadow(light_shadow_atlas, vec2(0.0), pssm_coord2.xy, pssm_coord2.z, light_clamp), pssm_blend); + } #endif -#ifdef USE_CONTACT_SHADOWS - if (shadow>0.01 && shadow_color_contact.a>0.0) { + attenuation *= shadow; - float contact_shadow = contact_shadow_compute(vertex,-light_direction_attenuation.xyz,shadow_color_contact.a); - shadow=min(shadow,contact_shadow); - } -#endif - light_attenuation=mix(mix(shadow_color_contact.rgb,vec3(1.0),shadow),vec3(1.0),pssm_fade); - - - } + } + } -#endif //LIGHT_DIRECTIONAL_SHADOW + light_compute(normal, + normalize(light_vec), + eye_position, + binormal, + tangent, + light_color.xyz * light_energy, + attenuation, + albedo, + transmission, + specular * light_specular, + roughness, + metallic, + rim, + rim_tint, + clearcoat, + clearcoat_gloss, + anisotropy, + diffuse_light, + specular_light); + } else if (light_type == LIGHT_TYPE_SPOT) { + + vec3 light_att = vec3(1.0); + + if (light_has_shadow > 0.5) { + highp vec4 splane = (light_shadow_matrix * vec4(vertex, 1.0)); + splane.xyz /= splane.w; + + float shadow = sample_shadow(light_shadow_atlas, vec2(0.0), splane.xy, splane.z, light_clamp); + + if (shadow > splane.z) { + } else { + light_att = vec3(0.0); + } -#ifdef USE_VERTEX_LIGHTING - diffuse_light*=mix(vec3(1.0),light_attenuation,diffuse_light_interp.a); - specular_light*=mix(vec3(1.0),light_attenuation,specular_light_interp.a); -#else - light_compute(normal,-light_direction_attenuation.xyz,eye_vec,binormal,tangent,light_color_energy.rgb,light_attenuation,albedo,transmission,light_params.z*specular_blob_intensity,roughness,metallic,rim,rim_tint,clearcoat,clearcoat_gloss,anisotropy,diffuse_light,specular_light); -#endif + } + vec3 light_rel_vec = light_position - vertex; + float light_length = length(light_rel_vec); + float normalized_distance = light_length / light_range; -#endif //#USE_LIGHT_DIRECTIONAL + float spot_attenuation = pow(1.0 - normalized_distance, light_attenuation.w); + vec3 spot_dir = light_direction; -#ifdef USE_GI_PROBES - gi_probes_compute(vertex,normal,roughness,env_reflection_light,ambient_light); + float spot_cutoff = light_spot_angle; -#endif + float scos = max(dot(-normalize(light_rel_vec), spot_dir), spot_cutoff); + float spot_rim = max(0.0001, (1.0 - scos) / (1.0 - spot_cutoff)); -#ifdef USE_FORWARD_LIGHTING + spot_attenuation *= 1.0 - pow(spot_rim, light_spot_attenuation); + light_att *= vec3(spot_attenuation); - highp vec4 reflection_accum = vec4(0.0,0.0,0.0,0.0); - highp vec4 ambient_accum = vec4(0.0,0.0,0.0,0.0); - for(int i=0;i<reflection_count;i++) { - reflection_process(reflection_indices[i],vertex,normal,binormal,tangent,roughness,anisotropy,ambient_light,env_reflection_light,reflection_accum,ambient_accum); + light_compute(normal, + normalize(light_rel_vec), + eye_position, + binormal, + tangent, + light_color.xyz * light_energy, + light_att, + albedo, + transmission, + specular * light_specular, + roughness, + metallic, + rim, + rim_tint, + clearcoat, + clearcoat_gloss, + anisotropy, + diffuse_light, + specular_light); } - if (reflection_accum.a>0.0) { - specular_light+=reflection_accum.rgb/reflection_accum.a; - } else { - specular_light+=env_reflection_light; - } - - if (ambient_accum.a>0.0) { - ambient_light+=ambient_accum.rgb/ambient_accum.a; - } + gl_FragColor = vec4(ambient_light + diffuse_light + specular_light, alpha); +#else +#ifdef RENDER_DEPTH +#else -#ifdef USE_VERTEX_LIGHTING +#ifdef USE_RADIANCE_MAP - diffuse_light*=albedo; -#else - for(int i=0;i<omni_light_count;i++) { - light_process_omni(omni_light_indices[i],vertex,eye_vec,normal,binormal,tangent,albedo,transmission,roughness,metallic,rim,rim_tint,clearcoat,clearcoat_gloss,anisotropy,specular_blob_intensity,diffuse_light,specular_light); - } + vec3 ref_vec = reflect(-eye_position, N); + ref_vec = normalize((radiance_inverse_xform * vec4(ref_vec, 0.0)).xyz); - for(int i=0;i<spot_light_count;i++) { - light_process_spot(spot_light_indices[i],vertex,eye_vec,normal,binormal,tangent,albedo,transmission,roughness,metallic,rim,rim_tint,clearcoat,clearcoat_gloss,anisotropy,specular_blob_intensity,diffuse_light,specular_light); - } + ref_vec.z *= -1.0; -#endif //USE_VERTEX_LIGHTING + env_reflection_light = textureCubeLod(radiance_map, ref_vec, roughness * RADIANCE_MAX_LOD).xyz * bg_energy; -#endif + { + vec3 ambient_dir = normalize((radiance_inverse_xform * vec4(normal, 0.0)).xyz); + vec3 env_ambient = textureCubeLod(radiance_map, ambient_dir, RADIANCE_MAX_LOD).xyz * bg_energy; + ambient_light = mix(ambient_color.rgb, env_ambient, ambient_sky_contribution); + } + ambient_light *= ambient_energy; -#ifdef RENDER_DEPTH -//nothing happens, so a tree-ssa optimizer will result in no fragment shader :) -#else + specular_light += env_reflection_light; - specular_light*=reflection_multiplier; - ambient_light*=albedo; //ambient must be multiplied by albedo at the end + ambient_light *= albedo; #if defined(ENABLE_AO) - ambient_light*=ao; - ao_light_affect = mix(1.0,ao,ao_light_affect); - specular_light*=ao_light_affect; - diffuse_light*=ao_light_affect; + ambient_light *= ao; + ao_light_affect = mix(1.0, ao, ao_light_affect); + specular_light *= ao_light_affect; + diffuse_light *= ao_light_affect; #endif + diffuse_light *= 1.0 - metallic; + ambient_light *= 1.0 - metallic; + // environment BRDF approximation - //energy conservation - diffuse_light *= 1.0-metallic; // TODO: avoid all diffuse and ambient light calculations when metallic == 1 up to this point - ambient_light *= 1.0-metallic; - - + // TODO shadeless { - -#if defined(DIFFUSE_TOON) - //simplify for toon, as - specular_light *= specular * metallic * albedo * 2.0; -#else - // Environment brdf approximation (Lazarov 2013) - // see https://www.unrealengine.com/en-US/blog/physically-based-shading-on-mobile const vec4 c0 = vec4(-1.0, -0.0275, -0.572, 0.022); const vec4 c1 = vec4( 1.0, 0.0425, 1.04, -0.04); vec4 r = roughness * c0 + c1; - float ndotv = clamp(dot(normal,eye_vec),0.0,1.0); + float ndotv = clamp(dot(normal,eye_position),0.0,1.0); float a004 = min( r.x * r.x, exp2( -9.28 * ndotv ) ) * r.x + r.y; vec2 AB = vec2( -1.04, 1.04 ) * a004 + r.zw; vec3 specular_color = metallic_to_specular_color(metallic, specular, albedo); specular_light *= AB.x * specular_color + AB.y; -#endif - } - if (fog_color_enabled.a > 0.5) { - float fog_amount=0.0; + gl_FragColor = vec4(ambient_light + diffuse_light + specular_light, alpha); + // gl_FragColor = vec4(normal, 1.0); - -#ifdef USE_LIGHT_DIRECTIONAL - - vec3 fog_color = mix( fog_color_enabled.rgb, fog_sun_color_amount.rgb,fog_sun_color_amount.a * pow(max( dot(normalize(vertex),-light_direction_attenuation.xyz), 0.0),8.0) ); #else - - vec3 fog_color = fog_color_enabled.rgb; + gl_FragColor = vec4(albedo, alpha); #endif - - //apply fog - - if (fog_depth_enabled) { - - float fog_z = smoothstep(fog_depth_begin,z_far,length(vertex)); - - fog_amount = pow(fog_z,fog_depth_curve); - if (fog_transmit_enabled) { - vec3 total_light = emission + ambient_light + specular_light + diffuse_light; - float transmit = pow(fog_z,fog_transmit_curve); - fog_color = mix(max(total_light,fog_color),fog_color,transmit); - } - } - - if (fog_height_enabled) { - float y = (camera_matrix * vec4(vertex,1.0)).y; - fog_amount = max(fog_amount,pow(smoothstep(fog_height_min,fog_height_max,y),fog_height_curve)); - } - - float rev_amount = 1.0 - fog_amount; - - - emission = emission * rev_amount + fog_color * fog_amount; - ambient_light*=rev_amount; - specular_light*rev_amount; - diffuse_light*=rev_amount; - - } - -#ifdef USE_MULTIPLE_RENDER_TARGETS - - -#ifdef SHADELESS - diffuse_buffer=vec4(albedo.rgb,0.0); - specular_buffer=vec4(0.0); - -#else - -#if defined(ENABLE_AO) - - float ambient_scale=0.0; // AO is supplied by material -#else - //approximate ambient scale for SSAO, since we will lack full ambient - float max_emission=max(emission.r,max(emission.g,emission.b)); - float max_ambient=max(ambient_light.r,max(ambient_light.g,ambient_light.b)); - float max_diffuse=max(diffuse_light.r,max(diffuse_light.g,diffuse_light.b)); - float total_ambient = max_ambient+max_diffuse+max_emission; - float ambient_scale = (total_ambient>0.0) ? (max_ambient+ambient_occlusion_affect_light*max_diffuse)/total_ambient : 0.0; -#endif //ENABLE_AO - - diffuse_buffer=vec4(emission+diffuse_light+ambient_light,ambient_scale); - specular_buffer=vec4(specular_light,metallic); - -#endif //SHADELESS - - normal_mr_buffer=vec4(normalize(normal)*0.5+0.5,roughness); - -#if defined (ENABLE_SSS) - sss_buffer = sss_strength; -#endif - - -#else //USE_MULTIPLE_RENDER_TARGETS - - -#ifdef SHADELESS - frag_color=vec4(albedo,alpha); -#else - frag_color=vec4(emission+ambient_light+diffuse_light+specular_light,alpha); -#endif //SHADELESS - - -#endif //USE_MULTIPLE_RENDER_TARGETS - +#endif // RENDER_DEPTH -#endif //RENDER_DEPTH +#endif // lighting } diff --git a/drivers/gles2/shaders/stdlib.glsl b/drivers/gles2/shaders/stdlib.glsl new file mode 100644 index 0000000000..8b30e097e6 --- /dev/null +++ b/drivers/gles2/shaders/stdlib.glsl @@ -0,0 +1,42 @@ + +vec2 select2(vec2 a, vec2 b, bvec2 c) +{ + vec2 ret; + + ret.x = c.x ? b.x : a.x; + ret.y = c.y ? b.y : a.y; + + return ret; +} + +vec3 select3(vec3 a, vec3 b, bvec3 c) +{ + vec3 ret; + + ret.x = c.x ? b.x : a.x; + ret.y = c.y ? b.y : a.y; + ret.z = c.z ? b.z : a.z; + + return ret; +} + +vec4 select4(vec4 a, vec4 b, bvec4 c) +{ + vec4 ret; + + ret.x = c.x ? b.x : a.x; + ret.y = c.y ? b.y : a.y; + ret.z = c.z ? b.z : a.z; + ret.w = c.w ? b.w : a.w; + + return ret; +} + + +highp vec4 texel2DFetch(highp sampler2D tex, ivec2 size, ivec2 coord) +{ + float x_coord = float(2 * coord.x + 1) / float(size.x * 2); + float y_coord = float(2 * coord.y + 1) / float(size.y * 2); + + return texture2DLod(tex, vec2(x_coord, y_coord), 0.0); +} diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp index f214943bcf..5e13bed198 100644 --- a/drivers/gles3/rasterizer_canvas_gles3.cpp +++ b/drivers/gles3/rasterizer_canvas_gles3.cpp @@ -211,6 +211,10 @@ RasterizerStorageGLES3::Texture *RasterizerCanvasGLES3::_bind_canvas_texture(con } else { + if (texture->redraw_if_visible) { //check before proxy, because this is usually used with proxies + VisualServerRaster::redraw_request(); + } + texture = texture->get_ptr(); if (texture->render_target) @@ -248,6 +252,10 @@ RasterizerStorageGLES3::Texture *RasterizerCanvasGLES3::_bind_canvas_texture(con } else { + if (normal_map->redraw_if_visible) { //check before proxy, because this is usually used with proxies + VisualServerRaster::redraw_request(); + } + normal_map = normal_map->get_ptr(); glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, normal_map->tex_id); @@ -1266,6 +1274,10 @@ void RasterizerCanvasGLES3::canvas_render_items(Item *p_item_list, int p_z, cons continue; } + if (t->redraw_if_visible) { //check before proxy, because this is usually used with proxies + VisualServerRaster::redraw_request(); + } + t = t->get_ptr(); if (storage->config.srgb_decode_supported && t->using_srgb) { @@ -1883,7 +1895,7 @@ void RasterizerCanvasGLES3::initialize() { } { - uint32_t poly_size = GLOBAL_DEF("rendering/limits/buffers/canvas_polygon_buffer_size_kb", 128); + uint32_t poly_size = GLOBAL_DEF_RST("rendering/limits/buffers/canvas_polygon_buffer_size_kb", 128); poly_size *= 1024; //kb poly_size = MAX(poly_size, (2 + 2 + 4) * 4 * sizeof(float)); glGenBuffers(1, &data.polygon_buffer); @@ -1930,7 +1942,7 @@ void RasterizerCanvasGLES3::initialize() { glGenVertexArrays(1, &data.polygon_buffer_pointer_array); - uint32_t index_size = GLOBAL_DEF("rendering/limits/buffers/canvas_polygon_index_buffer_size_kb", 128); + uint32_t index_size = GLOBAL_DEF_RST("rendering/limits/buffers/canvas_polygon_index_buffer_size_kb", 128); index_size *= 1024; //kb glGenBuffers(1, &data.polygon_index_buffer); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, data.polygon_index_buffer); diff --git a/drivers/gles3/rasterizer_gles3.cpp b/drivers/gles3/rasterizer_gles3.cpp index 1abdaa5f80..cb17695c5f 100644 --- a/drivers/gles3/rasterizer_gles3.cpp +++ b/drivers/gles3/rasterizer_gles3.cpp @@ -192,22 +192,15 @@ void RasterizerGLES3::initialize() { scene->initialize(); } -void RasterizerGLES3::begin_frame() { +void RasterizerGLES3::begin_frame(double frame_step) { - uint64_t tick = OS::get_singleton()->get_ticks_usec(); + time_total += frame_step; - double delta = double(tick - prev_ticks) / 1000000.0; - delta *= Engine::get_singleton()->get_time_scale(); - - time_total += delta; - - if (delta == 0) { + if (frame_step == 0) { //to avoid hiccups - delta = 0.001; + frame_step = 0.001; } - prev_ticks = tick; - double time_roll_over = GLOBAL_GET("rendering/limits/time/time_rollover_secs"); if (time_total > time_roll_over) time_total = 0; //roll over every day (should be customz @@ -217,9 +210,7 @@ void RasterizerGLES3::begin_frame() { storage->frame.time[2] = Math::fmod(time_total, 900); storage->frame.time[3] = Math::fmod(time_total, 60); storage->frame.count++; - storage->frame.delta = delta; - - storage->frame.prev_tick = tick; + storage->frame.delta = frame_step; storage->update_dirty_resources(); @@ -234,7 +225,7 @@ void RasterizerGLES3::set_current_render_target(RID p_render_target) { if (!p_render_target.is_valid() && storage->frame.current_rt && storage->frame.clear_request) { //handle pending clear request, if the framebuffer was not cleared glBindFramebuffer(GL_FRAMEBUFFER, storage->frame.current_rt->fbo); - print_line("unbind clear of: " + storage->frame.clear_request_color); + glClearColor( storage->frame.clear_request_color.r, storage->frame.clear_request_color.g, @@ -281,7 +272,7 @@ void RasterizerGLES3::set_boot_image(const Ref<Image> &p_image, const Color &p_c if (p_image.is_null() || p_image->empty()) return; - begin_frame(); + begin_frame(0.0); int window_w = OS::get_singleton()->get_video_mode(0).width; int window_h = OS::get_singleton()->get_video_mode(0).height; @@ -299,7 +290,7 @@ void RasterizerGLES3::set_boot_image(const Ref<Image> &p_image, const Color &p_c canvas->canvas_begin(); RID texture = storage->texture_create(); - storage->texture_allocate(texture, p_image->get_width(), p_image->get_height(), p_image->get_format(), VS::TEXTURE_FLAG_FILTER); + storage->texture_allocate(texture, p_image->get_width(), p_image->get_height(), 0, p_image->get_format(), VS::TEXTURE_TYPE_2D, VS::TEXTURE_FLAG_FILTER); storage->texture_set_data(texture, p_image); Rect2 imgrect(0, 0, p_image->get_width(), p_image->get_height()); @@ -333,28 +324,7 @@ void RasterizerGLES3::set_boot_image(const Ref<Image> &p_image, const Color &p_c storage->free(texture); // free since it's only one frame that stays there - if (OS::get_singleton()->is_layered_allowed()) { - if (OS::get_singleton()->get_window_per_pixel_transparency_enabled()) { -#if (defined WINDOWS_ENABLED) && !(defined UWP_ENABLED) - Size2 wndsize = OS::get_singleton()->get_layered_buffer_size(); - uint8_t *data = OS::get_singleton()->get_layered_buffer_data(); - if (data) { - glReadPixels(0, 0, wndsize.x, wndsize.y, GL_BGRA, GL_UNSIGNED_BYTE, data); - OS::get_singleton()->swap_layered_buffer(); - - return; - } -#endif - } else { - //clear alpha - glColorMask(false, false, false, true); - glClearColor(0, 0, 0, 1); - glClear(GL_COLOR_BUFFER_BIT); - glColorMask(true, true, true, true); - } - } - - OS::get_singleton()->swap_buffers(); + end_frame(true); } void RasterizerGLES3::blit_render_target_to_screen(RID p_render_target, const Rect2 &p_screen_rect, int p_screen) { @@ -451,7 +421,6 @@ RasterizerGLES3::RasterizerGLES3() { scene->storage = storage; storage->scene = scene; - prev_ticks = 0; time_total = 0; } diff --git a/drivers/gles3/rasterizer_gles3.h b/drivers/gles3/rasterizer_gles3.h index 5213101778..f4449ac0f9 100644 --- a/drivers/gles3/rasterizer_gles3.h +++ b/drivers/gles3/rasterizer_gles3.h @@ -44,7 +44,6 @@ class RasterizerGLES3 : public Rasterizer { RasterizerCanvasGLES3 *canvas; RasterizerSceneGLES3 *scene; - uint64_t prev_ticks; double time_total; public: @@ -55,7 +54,7 @@ public: virtual void set_boot_image(const Ref<Image> &p_image, const Color &p_color, bool p_scale); virtual void initialize(); - virtual void begin_frame(); + virtual void begin_frame(double frame_step); virtual void set_current_render_target(RID p_render_target); virtual void restore_render_target(); virtual void clear_render_target(const Color &p_color); diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp index 7c2af755cd..eebdbe9493 100644 --- a/drivers/gles3/rasterizer_scene_gles3.cpp +++ b/drivers/gles3/rasterizer_scene_gles3.cpp @@ -355,7 +355,7 @@ bool RasterizerSceneGLES3::shadow_atlas_update_light(RID p_atlas, RID p_light_in bool should_redraw = shadow_atlas->quadrants[q].shadows[s].version != p_light_version; if (!should_realloc) { - shadow_atlas->quadrants[q].shadows[s].version = p_light_version; + shadow_atlas->quadrants[q].shadows.write[s].version = p_light_version; //already existing, see if it should redraw or it's just OK return should_redraw; } @@ -365,7 +365,7 @@ bool RasterizerSceneGLES3::shadow_atlas_update_light(RID p_atlas, RID p_light_in //find a better place if (_shadow_atlas_find_shadow(shadow_atlas, valid_quadrants, valid_quadrant_count, shadow_atlas->quadrants[q].subdivision, tick, new_quadrant, new_shadow)) { //found a better place! - ShadowAtlas::Quadrant::Shadow *sh = &shadow_atlas->quadrants[new_quadrant].shadows[new_shadow]; + ShadowAtlas::Quadrant::Shadow *sh = &shadow_atlas->quadrants[new_quadrant].shadows.write[new_shadow]; if (sh->owner.is_valid()) { //is taken, but is invalid, erasing it shadow_atlas->shadow_owners.erase(sh->owner); @@ -374,8 +374,8 @@ bool RasterizerSceneGLES3::shadow_atlas_update_light(RID p_atlas, RID p_light_in } //erase previous - shadow_atlas->quadrants[q].shadows[s].version = 0; - shadow_atlas->quadrants[q].shadows[s].owner = RID(); + shadow_atlas->quadrants[q].shadows.write[s].version = 0; + shadow_atlas->quadrants[q].shadows.write[s].owner = RID(); sh->owner = p_light_intance; sh->alloc_tick = tick; @@ -395,7 +395,7 @@ bool RasterizerSceneGLES3::shadow_atlas_update_light(RID p_atlas, RID p_light_in //already existing, see if it should redraw or it's just OK - shadow_atlas->quadrants[q].shadows[s].version = p_light_version; + shadow_atlas->quadrants[q].shadows.write[s].version = p_light_version; return should_redraw; } @@ -405,7 +405,7 @@ bool RasterizerSceneGLES3::shadow_atlas_update_light(RID p_atlas, RID p_light_in //find a better place if (_shadow_atlas_find_shadow(shadow_atlas, valid_quadrants, valid_quadrant_count, -1, tick, new_quadrant, new_shadow)) { //found a better place! - ShadowAtlas::Quadrant::Shadow *sh = &shadow_atlas->quadrants[new_quadrant].shadows[new_shadow]; + ShadowAtlas::Quadrant::Shadow *sh = &shadow_atlas->quadrants[new_quadrant].shadows.write[new_shadow]; if (sh->owner.is_valid()) { //is taken, but is invalid, erasing it shadow_atlas->shadow_owners.erase(sh->owner); @@ -502,7 +502,7 @@ void RasterizerSceneGLES3::reflection_atlas_set_size(RID p_ref_atlas, int p_size //erase probes reference to this if (reflection_atlas->reflections[i].owner.is_valid()) { ReflectionProbeInstance *reflection_probe_instance = reflection_probe_instance_owner.getornull(reflection_atlas->reflections[i].owner); - reflection_atlas->reflections[i].owner = RID(); + reflection_atlas->reflections.write[i].owner = RID(); ERR_CONTINUE(!reflection_probe_instance); reflection_probe_instance->reflection_atlas_index = -1; @@ -574,7 +574,7 @@ void RasterizerSceneGLES3::reflection_atlas_set_subdivision(RID p_ref_atlas, int //erase probes reference to this if (reflection_atlas->reflections[i].owner.is_valid()) { ReflectionProbeInstance *reflection_probe_instance = reflection_probe_instance_owner.getornull(reflection_atlas->reflections[i].owner); - reflection_atlas->reflections[i].owner = RID(); + reflection_atlas->reflections.write[i].owner = RID(); ERR_CONTINUE(!reflection_probe_instance); reflection_probe_instance->reflection_atlas_index = -1; @@ -629,7 +629,7 @@ void RasterizerSceneGLES3::reflection_probe_release_atlas_index(RID p_instance) ERR_FAIL_COND(reflection_atlas->reflections[rpi->reflection_atlas_index].owner != rpi->self); - reflection_atlas->reflections[rpi->reflection_atlas_index].owner = RID(); + reflection_atlas->reflections.write[rpi->reflection_atlas_index].owner = RID(); rpi->reflection_atlas_index = -1; rpi->atlas = RID(); @@ -701,8 +701,8 @@ bool RasterizerSceneGLES3::reflection_probe_instance_begin_render(RID p_instance victim_rpi->reflection_atlas_index = -1; } - reflection_atlas->reflections[best_free].owner = p_instance; - reflection_atlas->reflections[best_free].last_frame = storage->frame.count; + reflection_atlas->reflections.write[best_free].owner = p_instance; + reflection_atlas->reflections.write[best_free].last_frame = storage->frame.count; rpi->reflection_atlas_index = best_free; rpi->atlas = p_reflection_atlas; @@ -1190,6 +1190,7 @@ bool RasterizerSceneGLES3::_setup_material(RasterizerStorageGLES3::Material *p_m int tc = p_material->textures.size(); RID *textures = p_material->textures.ptrw(); ShaderLanguage::ShaderNode::Uniform::Hint *texture_hints = p_material->shader->texture_hints.ptrw(); + const ShaderLanguage::DataType *texture_types = p_material->shader->texture_types.ptr(); state.current_main_tex = 0; @@ -1198,33 +1199,16 @@ bool RasterizerSceneGLES3::_setup_material(RasterizerStorageGLES3::Material *p_m glActiveTexture(GL_TEXTURE0 + i); GLenum target; - GLuint tex; + GLuint tex = 0; - RasterizerStorageGLES3::Texture *t = storage->texture_owner.getornull(textures[i]); + RasterizerStorageGLES3::Texture *t = storage->texture_owner.getptr(textures[i]); - if (!t) { - //check hints - target = GL_TEXTURE_2D; + if (t) { - switch (texture_hints[i]) { - case ShaderLanguage::ShaderNode::Uniform::HINT_BLACK_ALBEDO: - case ShaderLanguage::ShaderNode::Uniform::HINT_BLACK: { - tex = storage->resources.black_tex; - } break; - case ShaderLanguage::ShaderNode::Uniform::HINT_ANISO: { - tex = storage->resources.aniso_tex; - } break; - case ShaderLanguage::ShaderNode::Uniform::HINT_NORMAL: { - tex = storage->resources.normal_tex; - - } break; - default: { - tex = storage->resources.white_tex; - } break; + if (t->redraw_if_visible) { //must check before proxy because this is often used with proxies + VisualServerRaster::redraw_request(); } - } else { - t = t->get_ptr(); //resolve for proxies #ifdef TOOLS_ENABLED if (t->detect_3d) { @@ -1242,6 +1226,59 @@ bool RasterizerSceneGLES3::_setup_material(RasterizerStorageGLES3::Material *p_m target = t->target; tex = t->tex_id; + } else { + + switch (texture_types[i]) { + case ShaderLanguage::TYPE_ISAMPLER2D: + case ShaderLanguage::TYPE_USAMPLER2D: + case ShaderLanguage::TYPE_SAMPLER2D: { + target = GL_TEXTURE_2D; + + switch (texture_hints[i]) { + case ShaderLanguage::ShaderNode::Uniform::HINT_BLACK_ALBEDO: + case ShaderLanguage::ShaderNode::Uniform::HINT_BLACK: { + tex = storage->resources.black_tex; + } break; + case ShaderLanguage::ShaderNode::Uniform::HINT_ANISO: { + tex = storage->resources.aniso_tex; + } break; + case ShaderLanguage::ShaderNode::Uniform::HINT_NORMAL: { + tex = storage->resources.normal_tex; + + } break; + default: { + tex = storage->resources.white_tex; + } break; + } + + } break; + + case ShaderLanguage::TYPE_SAMPLERCUBE: { + // TODO + } break; + + case ShaderLanguage::TYPE_ISAMPLER3D: + case ShaderLanguage::TYPE_USAMPLER3D: + case ShaderLanguage::TYPE_SAMPLER3D: { + + target = GL_TEXTURE_3D; + + switch (texture_hints[i]) { + + // TODO + default: { + tex = storage->resources.white_tex_3d; + } break; + } + + } break; + + case ShaderLanguage::TYPE_ISAMPLER2DARRAY: + case ShaderLanguage::TYPE_USAMPLER2DARRAY: + case ShaderLanguage::TYPE_SAMPLER2DARRAY: { + // TODO + } break; + } } glBindTexture(target, tex); @@ -1569,6 +1606,11 @@ void RasterizerSceneGLES3::_render_geometry(RenderList::Element *e) { RasterizerStorageGLES3::Texture *t = storage->texture_owner.get(c.texture); t = t->get_ptr(); //resolve for proxies + + if (t->redraw_if_visible) { + VisualServerRaster::redraw_request(); + } + #ifdef TOOLS_ENABLED if (t->detect_3d) { t->detect_3d(t->detect_3d_ud); @@ -3838,8 +3880,8 @@ void RasterizerSceneGLES3::_post_process(Environment *env, const CameraMatrix &p state.exposure_shader.set_conditional(ExposureShaderGLES3::EXPOSURE_END, false); //last step, swap with the framebuffer exposure, so the right exposure is kept int he framebuffer - SWAP(exposure_shrink[exposure_shrink.size() - 1].fbo, storage->frame.current_rt->exposure.fbo); - SWAP(exposure_shrink[exposure_shrink.size() - 1].color, storage->frame.current_rt->exposure.color); + SWAP(exposure_shrink.write[exposure_shrink.size() - 1].fbo, storage->frame.current_rt->exposure.fbo); + SWAP(exposure_shrink.write[exposure_shrink.size() - 1].color, storage->frame.current_rt->exposure.color); glViewport(0, 0, storage->frame.current_rt->width, storage->frame.current_rt->height); @@ -4076,6 +4118,7 @@ void RasterizerSceneGLES3::render_scene(const Transform &p_cam_transform, const state.ubo_data.z_slope_scale = 0; state.ubo_data.shadow_dual_paraboloid_render_side = 0; state.ubo_data.shadow_dual_paraboloid_render_zfar = 0; + state.ubo_data.opaque_prepass_threshold = 0.99; p_cam_projection.get_viewport_size(state.ubo_data.viewport_size[0], state.ubo_data.viewport_size[1]); @@ -4688,6 +4731,7 @@ void RasterizerSceneGLES3::render_shadow(RID p_light, RID p_shadow_atlas, int p_ state.ubo_data.z_slope_scale = normal_bias; state.ubo_data.shadow_dual_paraboloid_render_side = dp_direction; state.ubo_data.shadow_dual_paraboloid_render_zfar = zfar; + state.ubo_data.opaque_prepass_threshold = 0.1; _setup_environment(NULL, light_projection, light_transform); @@ -4766,7 +4810,7 @@ bool RasterizerSceneGLES3::free(RID p_rid) { uint32_t q = (key >> ShadowAtlas::QUADRANT_SHIFT) & 0x3; uint32_t s = key & ShadowAtlas::SHADOW_INDEX_MASK; - shadow_atlas->quadrants[q].shadows[s].owner = RID(); + shadow_atlas->quadrants[q].shadows.write[s].owner = RID(); shadow_atlas->shadow_owners.erase(p_rid); } @@ -4856,7 +4900,7 @@ void RasterizerSceneGLES3::initialize() { glBufferData(GL_UNIFORM_BUFFER, sizeof(State::EnvironmentRadianceUBO), &state.env_radiance_ubo, GL_DYNAMIC_DRAW); glBindBuffer(GL_UNIFORM_BUFFER, 0); - render_list.max_elements = GLOBAL_DEF("rendering/limits/rendering/max_renderable_elements", (int)RenderList::DEFAULT_MAX_ELEMENTS); + render_list.max_elements = GLOBAL_DEF_RST("rendering/limits/rendering/max_renderable_elements", (int)RenderList::DEFAULT_MAX_ELEMENTS); if (render_list.max_elements > 1000000) render_list.max_elements = 1000000; if (render_list.max_elements < 1024) diff --git a/drivers/gles3/rasterizer_scene_gles3.h b/drivers/gles3/rasterizer_scene_gles3.h index 524212b9c1..cf387a69bc 100644 --- a/drivers/gles3/rasterizer_scene_gles3.h +++ b/drivers/gles3/rasterizer_scene_gles3.h @@ -141,6 +141,7 @@ public: float subsurface_scatter_width; float ambient_occlusion_affect_light; float ambient_occlusion_affect_ssao; + float opaque_prepass_threshold; uint32_t fog_depth_enabled; float fog_depth_begin; @@ -152,7 +153,7 @@ public: float fog_height_max; float fog_height_curve; // make sure this struct is padded to be a multiple of 16 bytes for webgl - float pad[3]; + float pad[2]; } ubo_data; diff --git a/drivers/gles3/rasterizer_storage_gles3.cpp b/drivers/gles3/rasterizer_storage_gles3.cpp index 4a3ebf7a7c..3697cccef4 100644 --- a/drivers/gles3/rasterizer_storage_gles3.cpp +++ b/drivers/gles3/rasterizer_storage_gles3.cpp @@ -118,10 +118,11 @@ void glTexStorage2DCustom(GLenum target, GLsizei levels, GLenum internalformat, GLuint RasterizerStorageGLES3::system_fbo = 0; -Ref<Image> RasterizerStorageGLES3::_get_gl_image_and_format(const Ref<Image> &p_image, Image::Format p_format, uint32_t p_flags, GLenum &r_gl_format, GLenum &r_gl_internal_format, GLenum &r_gl_type, bool &r_compressed, bool &srgb) { +Ref<Image> RasterizerStorageGLES3::_get_gl_image_and_format(const Ref<Image> &p_image, Image::Format p_format, uint32_t p_flags, Image::Format &r_real_format, GLenum &r_gl_format, GLenum &r_gl_internal_format, GLenum &r_gl_type, bool &r_compressed, bool &srgb) const { r_compressed = false; r_gl_format = 0; + r_real_format = p_format; Ref<Image> image = p_image; srgb = false; @@ -565,6 +566,7 @@ Ref<Image> RasterizerStorageGLES3::_get_gl_image_and_format(const Ref<Image> &p_ r_gl_internal_format = (config.srgb_decode_supported || (p_flags & VS::TEXTURE_FLAG_CONVERT_TO_LINEAR)) ? GL_SRGB8_ALPHA8 : GL_RGBA8; r_gl_type = GL_UNSIGNED_BYTE; r_compressed = false; + r_real_format = Image::FORMAT_RGBA8; srgb = true; return image; @@ -595,7 +597,7 @@ RID RasterizerStorageGLES3::texture_create() { return texture_owner.make_rid(texture); } -void RasterizerStorageGLES3::texture_allocate(RID p_texture, int p_width, int p_height, Image::Format p_format, uint32_t p_flags) { +void RasterizerStorageGLES3::texture_allocate(RID p_texture, int p_width, int p_height, int p_depth_3d, Image::Format p_format, VisualServer::TextureType p_type, uint32_t p_flags) { GLenum format; GLenum internal_format; @@ -612,15 +614,38 @@ void RasterizerStorageGLES3::texture_allocate(RID p_texture, int p_width, int p_ ERR_FAIL_COND(!texture); texture->width = p_width; texture->height = p_height; + texture->depth = p_depth_3d; texture->format = p_format; texture->flags = p_flags; texture->stored_cube_sides = 0; - texture->target = (p_flags & VS::TEXTURE_FLAG_CUBEMAP) ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D; - _get_gl_image_and_format(Ref<Image>(), texture->format, texture->flags, format, internal_format, type, compressed, srgb); + texture->type = p_type; + + switch (p_type) { + case VS::TEXTURE_TYPE_2D: { + texture->target = GL_TEXTURE_2D; + texture->images.resize(1); + } break; + case VS::TEXTURE_TYPE_CUBEMAP: { + texture->target = GL_TEXTURE_CUBE_MAP; + texture->images.resize(6); + } break; + case VS::TEXTURE_TYPE_2D_ARRAY: { + texture->target = GL_TEXTURE_2D_ARRAY; + texture->images.resize(p_depth_3d); + } break; + case VS::TEXTURE_TYPE_3D: { + texture->target = GL_TEXTURE_3D; + texture->images.resize(p_depth_3d); + } break; + } + + Image::Format real_format; + _get_gl_image_and_format(Ref<Image>(), texture->format, texture->flags, real_format, format, internal_format, type, compressed, srgb); texture->alloc_width = texture->width; texture->alloc_height = texture->height; + texture->alloc_depth = texture->depth; texture->gl_format_cache = format; texture->gl_type_cache = type; @@ -633,7 +658,34 @@ void RasterizerStorageGLES3::texture_allocate(RID p_texture, int p_width, int p_ glActiveTexture(GL_TEXTURE0); glBindTexture(texture->target, texture->tex_id); - if (p_flags & VS::TEXTURE_FLAG_USED_FOR_STREAMING) { + if (p_type == VS::TEXTURE_TYPE_3D || p_type == VS::TEXTURE_TYPE_2D_ARRAY) { + + int width = p_width; + int height = p_height; + int depth = p_depth_3d; + + int mipmaps = 0; + + while (width != 1 && height != 1) { + glTexImage3D(texture->target, 0, internal_format, width, height, depth, 0, format, type, NULL); + + width = MAX(1, width / 2); + height = MAX(1, height / 2); + + if (p_type == VS::TEXTURE_TYPE_3D) { + depth = MAX(1, depth / 2); + } + + mipmaps++; + + if (!(p_flags & VS::TEXTURE_FLAG_MIPMAPS)) + break; + } + + glTexParameteri(texture->target, GL_TEXTURE_BASE_LEVEL, 0); + glTexParameteri(texture->target, GL_TEXTURE_MAX_LEVEL, mipmaps - 1); + + } else if (p_flags & VS::TEXTURE_FLAG_USED_FOR_STREAMING) { //prealloc if video glTexImage2D(texture->target, 0, internal_format, p_width, p_height, 0, format, type, NULL); } @@ -641,7 +693,7 @@ void RasterizerStorageGLES3::texture_allocate(RID p_texture, int p_width, int p_ texture->active = true; } -void RasterizerStorageGLES3::texture_set_data(RID p_texture, const Ref<Image> &p_image, VS::CubeMapSide p_cube_side) { +void RasterizerStorageGLES3::texture_set_data(RID p_texture, const Ref<Image> &p_image, int p_layer) { Texture *texture = texture_owner.get(p_texture); @@ -658,10 +710,11 @@ void RasterizerStorageGLES3::texture_set_data(RID p_texture, const Ref<Image> &p bool srgb; if (config.keep_original_textures && !(texture->flags & VS::TEXTURE_FLAG_USED_FOR_STREAMING)) { - texture->images[p_cube_side] = p_image; + texture->images.write[p_layer] = p_image; } - Ref<Image> img = _get_gl_image_and_format(p_image, p_image->get_format(), texture->flags, format, internal_format, type, compressed, srgb); + Image::Format real_format; + Ref<Image> img = _get_gl_image_and_format(p_image, p_image->get_format(), texture->flags, real_format, format, internal_format, type, compressed, srgb); if (config.shrink_textures_x2 && (p_image->has_mipmaps() || !p_image->is_compressed()) && !(texture->flags & VS::TEXTURE_FLAG_USED_FOR_STREAMING)) { @@ -677,7 +730,23 @@ void RasterizerStorageGLES3::texture_set_data(RID p_texture, const Ref<Image> &p } }; - GLenum blit_target = (texture->target == GL_TEXTURE_CUBE_MAP) ? _cube_side_enum[p_cube_side] : GL_TEXTURE_2D; + GLenum blit_target; + + switch (texture->type) { + case VS::TEXTURE_TYPE_2D: { + blit_target = GL_TEXTURE_2D; + } break; + case VS::TEXTURE_TYPE_CUBEMAP: { + ERR_FAIL_INDEX(p_layer, 6); + blit_target = _cube_side_enum[p_layer]; + } break; + case VS::TEXTURE_TYPE_2D_ARRAY: { + blit_target = GL_TEXTURE_2D_ARRAY; + } break; + case VS::TEXTURE_TYPE_3D: { + blit_target = GL_TEXTURE_3D; + } break; + } texture->data_size = img->get_data().size(); PoolVector<uint8_t>::Read read = img->get_data().read(); @@ -785,20 +854,36 @@ void RasterizerStorageGLES3::texture_set_data(RID p_texture, const Ref<Image> &p //print_line("mipmap: "+itos(i)+" size: "+itos(size)+" w: "+itos(mm_w)+", h: "+itos(mm_h)); - if (texture->compressed) { - glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + if (texture->type == VS::TEXTURE_TYPE_2D || texture->type == VS::TEXTURE_TYPE_CUBEMAP) { - int bw = w; - int bh = h; + if (texture->compressed) { + glPixelStorei(GL_UNPACK_ALIGNMENT, 4); - glCompressedTexImage2D(blit_target, i, internal_format, bw, bh, 0, size, &read[ofs]); + int bw = w; + int bh = h; + glCompressedTexImage2D(blit_target, i, internal_format, bw, bh, 0, size, &read[ofs]); + + } else { + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + if (texture->flags & VS::TEXTURE_FLAG_USED_FOR_STREAMING) { + glTexSubImage2D(blit_target, i, 0, 0, w, h, format, type, &read[ofs]); + } else { + glTexImage2D(blit_target, i, internal_format, w, h, 0, format, type, &read[ofs]); + } + } } else { - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - if (texture->flags & VS::TEXTURE_FLAG_USED_FOR_STREAMING) { - glTexSubImage2D(blit_target, i, 0, 0, w, h, format, type, &read[ofs]); + if (texture->compressed) { + glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + + int bw = w; + int bh = h; + + glCompressedTexSubImage3D(blit_target, i, 0, 0, p_layer, bw, bh, 1, internal_format, size, &read[ofs]); } else { - glTexImage2D(blit_target, i, internal_format, w, h, 0, format, type, &read[ofs]); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + glTexSubImage3D(blit_target, i, 0, 0, p_layer, w, h, 1, format, type, &read[ofs]); } } tsize += size; @@ -813,14 +898,17 @@ void RasterizerStorageGLES3::texture_set_data(RID p_texture, const Ref<Image> &p //printf("texture: %i x %i - size: %i - total: %i\n",texture->width,texture->height,tsize,_rinfo.texture_mem); - texture->stored_cube_sides |= (1 << p_cube_side); + texture->stored_cube_sides |= (1 << p_layer); - if ((texture->flags & VS::TEXTURE_FLAG_MIPMAPS) && mipmaps == 1 && !texture->ignore_mipmaps && (!(texture->flags & VS::TEXTURE_FLAG_CUBEMAP) || texture->stored_cube_sides == (1 << 6) - 1)) { + if ((texture->type == VS::TEXTURE_TYPE_2D || texture->type == VS::TEXTURE_TYPE_CUBEMAP) && (texture->flags & VS::TEXTURE_FLAG_MIPMAPS) && mipmaps == 1 && !texture->ignore_mipmaps && (texture->type != VS::TEXTURE_TYPE_CUBEMAP || texture->stored_cube_sides == (1 << 6) - 1)) { //generate mipmaps if they were requested and the image does not contain them glGenerateMipmap(texture->target); } else if (mipmaps > 1) { glTexParameteri(texture->target, GL_TEXTURE_BASE_LEVEL, 0); glTexParameteri(texture->target, GL_TEXTURE_MAX_LEVEL, mipmaps - 1); + } else { + glTexParameteri(texture->target, GL_TEXTURE_BASE_LEVEL, 0); + glTexParameteri(texture->target, GL_TEXTURE_MAX_LEVEL, 0); } texture->mipmaps = mipmaps; @@ -831,7 +919,7 @@ void RasterizerStorageGLES3::texture_set_data(RID p_texture, const Ref<Image> &p // Uploads pixel data to a sub-region of a texture, for the specified mipmap. // The texture pixels must have been allocated before, because most features seen in texture_set_data() make no sense in a partial update. // TODO If we want this to be usable without pre-filling pixels with a full image, we have to call glTexImage2D() with null data. -void RasterizerStorageGLES3::texture_set_data_partial(RID p_texture, const Ref<Image> &p_image, int src_x, int src_y, int src_w, int src_h, int dst_x, int dst_y, int p_dst_mip, VS::CubeMapSide p_cube_side) { +void RasterizerStorageGLES3::texture_set_data_partial(RID p_texture, const Ref<Image> &p_image, int src_x, int src_y, int src_w, int src_h, int dst_x, int dst_y, int p_dst_mip, int p_layer) { Texture *texture = texture_owner.get(p_texture); @@ -857,9 +945,26 @@ void RasterizerStorageGLES3::texture_set_data_partial(RID p_texture, const Ref<I p_sub_img = p_image->get_rect(Rect2(src_x, src_y, src_w, src_h)); } - Ref<Image> img = _get_gl_image_and_format(p_sub_img, p_sub_img->get_format(), texture->flags, format, internal_format, type, compressed, srgb); + Image::Format real_format; + Ref<Image> img = _get_gl_image_and_format(p_sub_img, p_sub_img->get_format(), texture->flags, real_format, format, internal_format, type, compressed, srgb); - GLenum blit_target = (texture->target == GL_TEXTURE_CUBE_MAP) ? _cube_side_enum[p_cube_side] : GL_TEXTURE_2D; + GLenum blit_target; + + switch (texture->type) { + case VS::TEXTURE_TYPE_2D: { + blit_target = GL_TEXTURE_2D; + } break; + case VS::TEXTURE_TYPE_CUBEMAP: { + ERR_FAIL_INDEX(p_layer, 6); + blit_target = _cube_side_enum[p_layer]; + } break; + case VS::TEXTURE_TYPE_2D_ARRAY: { + blit_target = GL_TEXTURE_2D_ARRAY; + } break; + case VS::TEXTURE_TYPE_3D: { + blit_target = GL_TEXTURE_3D; + } break; + } PoolVector<uint8_t>::Read read = img->get_data().read(); @@ -869,18 +974,38 @@ void RasterizerStorageGLES3::texture_set_data_partial(RID p_texture, const Ref<I int src_data_size = img->get_data().size(); int src_ofs = 0; - if (texture->compressed) { - glPixelStorei(GL_UNPACK_ALIGNMENT, 4); - glCompressedTexSubImage2D(blit_target, p_dst_mip, dst_x, dst_y, src_w, src_h, internal_format, src_data_size, &read[src_ofs]); + if (texture->type == VS::TEXTURE_TYPE_2D || texture->type == VS::TEXTURE_TYPE_CUBEMAP) { + if (texture->compressed) { + glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + glCompressedTexSubImage2D(blit_target, p_dst_mip, dst_x, dst_y, src_w, src_h, internal_format, src_data_size, &read[src_ofs]); + + } else { + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + // `format` has to match the internal_format used when the texture was created + glTexSubImage2D(blit_target, p_dst_mip, dst_x, dst_y, src_w, src_h, format, type, &read[src_ofs]); + } + } else { + if (texture->compressed) { + glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + glCompressedTexSubImage3D(blit_target, p_dst_mip, dst_x, dst_y, p_layer, src_w, src_h, 1, format, src_data_size, &read[src_ofs]); + } else { + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + // `format` has to match the internal_format used when the texture was created + glTexSubImage3D(blit_target, p_dst_mip, dst_x, dst_y, p_layer, src_w, src_h, 1, format, type, &read[src_ofs]); + } + } + + if (texture->flags & VS::TEXTURE_FLAG_FILTER) { + + glTexParameteri(texture->target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // Linear Filtering } else { - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - // `format` has to match the internal_format used when the texture was created - glTexSubImage2D(blit_target, p_dst_mip, dst_x, dst_y, src_w, src_h, format, type, &read[src_ofs]); + + glTexParameteri(texture->target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // raw Filtering } } -Ref<Image> RasterizerStorageGLES3::texture_get_data(RID p_texture, VS::CubeMapSide p_cube_side) const { +Ref<Image> RasterizerStorageGLES3::texture_get_data(RID p_texture, int p_layer) const { Texture *texture = texture_owner.get(p_texture); @@ -888,15 +1013,23 @@ Ref<Image> RasterizerStorageGLES3::texture_get_data(RID p_texture, VS::CubeMapSi ERR_FAIL_COND_V(!texture->active, Ref<Image>()); ERR_FAIL_COND_V(texture->data_size == 0 && !texture->render_target, Ref<Image>()); - if (!texture->images[p_cube_side].is_null()) { - return texture->images[p_cube_side]; + if (texture->type == VS::TEXTURE_TYPE_CUBEMAP && p_layer < 6 && !texture->images[p_layer].is_null()) { + return texture->images[p_layer]; } #ifdef GLES_OVER_GL + Image::Format real_format; + GLenum gl_format; + GLenum gl_internal_format; + GLenum gl_type; + bool compressed; + bool srgb; + _get_gl_image_and_format(Ref<Image>(), texture->format, texture->flags, real_format, gl_format, gl_internal_format, gl_type, compressed, srgb); + PoolVector<uint8_t> data; - int data_size = Image::get_image_data_size(texture->alloc_width, texture->alloc_height, texture->format, texture->mipmaps > 1 ? -1 : 0); + int data_size = Image::get_image_data_size(texture->alloc_width, texture->alloc_height, real_format, texture->mipmaps > 1 ? -1 : 0); data.resize(data_size * 2); //add some memory at the end, just in case for buggy drivers PoolVector<uint8_t>::Write wb = data.write(); @@ -913,7 +1046,7 @@ Ref<Image> RasterizerStorageGLES3::texture_get_data(RID p_texture, VS::CubeMapSi int ofs = 0; if (i > 0) { - ofs = Image::get_image_data_size(texture->alloc_width, texture->alloc_height, texture->format, i - 1); + ofs = Image::get_image_data_size(texture->alloc_width, texture->alloc_height, real_format, i - 1); } if (texture->compressed) { @@ -949,7 +1082,7 @@ Ref<Image> RasterizerStorageGLES3::texture_get_data(RID p_texture, VS::CubeMapSi (a | a << 2 | a << 4 | a << 6) << 24; } } else { - img_format = texture->format; + img_format = real_format; } wb = PoolVector<uint8_t>::Write(); @@ -977,10 +1110,10 @@ void RasterizerStorageGLES3::texture_set_flags(RID p_texture, uint32_t p_flags) bool had_mipmaps = texture->flags & VS::TEXTURE_FLAG_MIPMAPS; + texture->flags = p_flags; + glActiveTexture(GL_TEXTURE0); glBindTexture(texture->target, texture->tex_id); - uint32_t cube = texture->flags & VS::TEXTURE_FLAG_CUBEMAP; - texture->flags = p_flags | cube; // can't remove a cube from being a cube if (((texture->flags & VS::TEXTURE_FLAG_REPEAT) || (texture->flags & VS::TEXTURE_FLAG_MIRRORED_REPEAT)) && texture->target != GL_TEXTURE_CUBE_MAP) { @@ -1058,6 +1191,14 @@ Image::Format RasterizerStorageGLES3::texture_get_format(RID p_texture) const { return texture->format; } + +VisualServer::TextureType RasterizerStorageGLES3::texture_get_type(RID p_texture) const { + Texture *texture = texture_owner.get(p_texture); + + ERR_FAIL_COND_V(!texture, VS::TEXTURE_TYPE_2D); + + return texture->type; +} uint32_t RasterizerStorageGLES3::texture_get_texid(RID p_texture) const { Texture *texture = texture_owner.get(p_texture); @@ -1083,7 +1224,16 @@ uint32_t RasterizerStorageGLES3::texture_get_height(RID p_texture) const { return texture->height; } -void RasterizerStorageGLES3::texture_set_size_override(RID p_texture, int p_width, int p_height) { +uint32_t RasterizerStorageGLES3::texture_get_depth(RID p_texture) const { + + Texture *texture = texture_owner.get(p_texture); + + ERR_FAIL_COND_V(!texture, 0); + + return texture->depth; +} + +void RasterizerStorageGLES3::texture_set_size_override(RID p_texture, int p_width, int p_height, int p_depth) { Texture *texture = texture_owner.get(p_texture); @@ -1123,8 +1273,9 @@ void RasterizerStorageGLES3::texture_debug_usage(List<VS::TextureInfo> *r_info) VS::TextureInfo tinfo; tinfo.path = t->path; tinfo.format = t->format; - tinfo.size.x = t->alloc_width; - tinfo.size.y = t->alloc_height; + tinfo.width = t->alloc_width; + tinfo.height = t->alloc_height; + tinfo.depth = 0; tinfo.bytes = t->total_data_size; r_info->push_back(tinfo); } @@ -1169,7 +1320,7 @@ RID RasterizerStorageGLES3::texture_create_radiance_cubemap(RID p_source, int p_ Texture *texture = texture_owner.get(p_source); ERR_FAIL_COND_V(!texture, RID()); - ERR_FAIL_COND_V(!(texture->flags & VS::TEXTURE_FLAG_CUBEMAP), RID()); + ERR_FAIL_COND_V(texture->type != VS::TEXTURE_TYPE_CUBEMAP, RID()); bool use_float = config.hdr_supported; @@ -1285,7 +1436,8 @@ RID RasterizerStorageGLES3::texture_create_radiance_cubemap(RID p_source, int p_ Texture *ctex = memnew(Texture); - ctex->flags = VS::TEXTURE_FLAG_CUBEMAP | VS::TEXTURE_FLAG_MIPMAPS | VS::TEXTURE_FLAG_FILTER; + ctex->type = VS::TEXTURE_TYPE_CUBEMAP; + ctex->flags = VS::TEXTURE_FLAG_MIPMAPS | VS::TEXTURE_FLAG_FILTER; ctex->width = p_resolution; ctex->height = p_resolution; ctex->alloc_width = p_resolution; @@ -1328,6 +1480,13 @@ void RasterizerStorageGLES3::texture_set_proxy(RID p_texture, RID p_proxy) { } } +void RasterizerStorageGLES3::texture_set_force_redraw_if_visible(RID p_texture, bool p_enable) { + + Texture *texture = texture_owner.get(p_texture); + ERR_FAIL_COND(!texture); + texture->redraw_if_visible = p_enable; +} + RID RasterizerStorageGLES3::sky_create() { Sky *sky = memnew(Sky); @@ -1758,6 +1917,7 @@ void RasterizerStorageGLES3::_update_shader(Shader *p_shader) const { p_shader->ubo_offsets = gen_code.uniform_offsets; p_shader->texture_count = gen_code.texture_uniforms.size(); p_shader->texture_hints = gen_code.texture_hints; + p_shader->texture_types = gen_code.texture_types; p_shader->uses_vertex_time = gen_code.uses_vertex_time; p_shader->uses_fragment_time = gen_code.uses_fragment_time; @@ -1868,6 +2028,13 @@ void RasterizerStorageGLES3::shader_get_param_list(RID p_shader, List<PropertyIn pi.hint = PROPERTY_HINT_RESOURCE_TYPE; pi.hint_string = "Texture"; } break; + case ShaderLanguage::TYPE_SAMPLER3D: + case ShaderLanguage::TYPE_ISAMPLER3D: + case ShaderLanguage::TYPE_USAMPLER3D: { + pi.type = Variant::OBJECT; + pi.hint = PROPERTY_HINT_RESOURCE_TYPE; + pi.hint_string = "Texture3D"; + } break; case ShaderLanguage::TYPE_SAMPLERCUBE: { pi.type = Variant::OBJECT; @@ -2642,6 +2809,7 @@ void RasterizerStorageGLES3::_update_material(Material *material) { //set up the texture array, for easy access when it needs to be drawn if (material->shader && material->shader->texture_count) { + material->texture_is_3d.resize(material->shader->texture_count); material->textures.resize(material->shader->texture_count); for (Map<StringName, ShaderLanguage::ShaderNode::Uniform>::Element *E = material->shader->uniforms.front(); E; E = E->next()) { @@ -2651,6 +2819,16 @@ void RasterizerStorageGLES3::_update_material(Material *material) { RID texture; + switch (E->get().type) { + case ShaderLanguage::TYPE_SAMPLER3D: + case ShaderLanguage::TYPE_SAMPLER2DARRAY: { + material->texture_is_3d.write[E->get().texture_order] = true; + } break; + default: { + material->texture_is_3d.write[E->get().texture_order] = false; + } break; + } + Map<StringName, Variant>::Element *V = material->params.find(E->key()); if (V) { texture = V->get(); @@ -2663,11 +2841,12 @@ void RasterizerStorageGLES3::_update_material(Material *material) { } } - material->textures[E->get().texture_order] = texture; + material->textures.write[E->get().texture_order] = texture; } } else { material->textures.clear(); + material->texture_is_3d.clear(); } } @@ -2968,9 +3147,9 @@ void RasterizerStorageGLES3::mesh_add_surface(RID p_mesh, uint32_t p_format, VS: for (int i = 0; i < surface->skeleton_bone_used.size(); i++) { if (surface->skeleton_bone_aabb[i].size.x < 0 || surface->skeleton_bone_aabb[i].size.y < 0 || surface->skeleton_bone_aabb[i].size.z < 0) { - surface->skeleton_bone_used[i] = false; + surface->skeleton_bone_used.write[i] = false; } else { - surface->skeleton_bone_used[i] = true; + surface->skeleton_bone_used.write[i] = true; } } @@ -3233,7 +3412,7 @@ void RasterizerStorageGLES3::mesh_surface_update_region(RID p_mesh, int p_surfac PoolVector<uint8_t>::Read r = p_data.read(); - glBindBuffer(GL_ARRAY_BUFFER, mesh->surfaces[p_surface]->array_id); + glBindBuffer(GL_ARRAY_BUFFER, mesh->surfaces[p_surface]->vertex_id); glBufferSubData(GL_ARRAY_BUFFER, p_offset, total_size, r.ptr()); glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind } @@ -3397,6 +3576,7 @@ Vector<PoolVector<uint8_t> > RasterizerStorageGLES3::mesh_surface_get_blend_shap return bsarr; } + Vector<AABB> RasterizerStorageGLES3::mesh_surface_get_skeleton_aabb(RID p_mesh, int p_surface) const { const Mesh *mesh = mesh_owner.getornull(p_mesh); @@ -3448,6 +3628,7 @@ void RasterizerStorageGLES3::mesh_remove_surface(RID p_mesh, int p_surface) { mesh->instance_change_notify(); } + int RasterizerStorageGLES3::mesh_get_surface_count(RID p_mesh) const { const Mesh *mesh = mesh_owner.getornull(p_mesh); @@ -3461,6 +3642,7 @@ void RasterizerStorageGLES3::mesh_set_custom_aabb(RID p_mesh, const AABB &p_aabb ERR_FAIL_COND(!mesh); mesh->custom_aabb = p_aabb; + mesh->instance_change_notify(); } AABB RasterizerStorageGLES3::mesh_get_custom_aabb(RID p_mesh) const { @@ -3868,29 +4050,29 @@ void RasterizerStorageGLES3::multimesh_allocate(RID p_multimesh, int p_instances int custom_data_from = 0; if (multimesh->transform_format == VS::MULTIMESH_TRANSFORM_2D) { - multimesh->data[i + 0] = 1.0; - multimesh->data[i + 1] = 0.0; - multimesh->data[i + 2] = 0.0; - multimesh->data[i + 3] = 0.0; - multimesh->data[i + 4] = 0.0; - multimesh->data[i + 5] = 1.0; - multimesh->data[i + 6] = 0.0; - multimesh->data[i + 7] = 0.0; + multimesh->data.write[i + 0] = 1.0; + multimesh->data.write[i + 1] = 0.0; + multimesh->data.write[i + 2] = 0.0; + multimesh->data.write[i + 3] = 0.0; + multimesh->data.write[i + 4] = 0.0; + multimesh->data.write[i + 5] = 1.0; + multimesh->data.write[i + 6] = 0.0; + multimesh->data.write[i + 7] = 0.0; color_from = 8; custom_data_from = 8; } else { - multimesh->data[i + 0] = 1.0; - multimesh->data[i + 1] = 0.0; - multimesh->data[i + 2] = 0.0; - multimesh->data[i + 3] = 0.0; - multimesh->data[i + 4] = 0.0; - multimesh->data[i + 5] = 1.0; - multimesh->data[i + 6] = 0.0; - multimesh->data[i + 7] = 0.0; - multimesh->data[i + 8] = 0.0; - multimesh->data[i + 9] = 0.0; - multimesh->data[i + 10] = 1.0; - multimesh->data[i + 11] = 0.0; + multimesh->data.write[i + 0] = 1.0; + multimesh->data.write[i + 1] = 0.0; + multimesh->data.write[i + 2] = 0.0; + multimesh->data.write[i + 3] = 0.0; + multimesh->data.write[i + 4] = 0.0; + multimesh->data.write[i + 5] = 1.0; + multimesh->data.write[i + 6] = 0.0; + multimesh->data.write[i + 7] = 0.0; + multimesh->data.write[i + 8] = 0.0; + multimesh->data.write[i + 9] = 0.0; + multimesh->data.write[i + 10] = 1.0; + multimesh->data.write[i + 11] = 0.0; color_from = 12; custom_data_from = 12; } @@ -3905,14 +4087,14 @@ void RasterizerStorageGLES3::multimesh_allocate(RID p_multimesh, int p_instances } cu; cu.colu = 0xFFFFFFFF; - multimesh->data[i + color_from + 0] = cu.colf; + multimesh->data.write[i + color_from + 0] = cu.colf; custom_data_from = color_from + 1; } else if (multimesh->color_format == VS::MULTIMESH_COLOR_FLOAT) { - multimesh->data[i + color_from + 0] = 1.0; - multimesh->data[i + color_from + 1] = 1.0; - multimesh->data[i + color_from + 2] = 1.0; - multimesh->data[i + color_from + 3] = 1.0; + multimesh->data.write[i + color_from + 0] = 1.0; + multimesh->data.write[i + color_from + 1] = 1.0; + multimesh->data.write[i + color_from + 2] = 1.0; + multimesh->data.write[i + color_from + 3] = 1.0; custom_data_from = color_from + 4; } @@ -3926,13 +4108,13 @@ void RasterizerStorageGLES3::multimesh_allocate(RID p_multimesh, int p_instances } cu; cu.colu = 0; - multimesh->data[i + custom_data_from + 0] = cu.colf; + multimesh->data.write[i + custom_data_from + 0] = cu.colf; } else if (multimesh->custom_data_format == VS::MULTIMESH_CUSTOM_DATA_FLOAT) { - multimesh->data[i + custom_data_from + 0] = 0.0; - multimesh->data[i + custom_data_from + 1] = 0.0; - multimesh->data[i + custom_data_from + 2] = 0.0; - multimesh->data[i + custom_data_from + 3] = 0.0; + multimesh->data.write[i + custom_data_from + 0] = 0.0; + multimesh->data.write[i + custom_data_from + 1] = 0.0; + multimesh->data.write[i + custom_data_from + 2] = 0.0; + multimesh->data.write[i + custom_data_from + 3] = 0.0; } } @@ -3994,7 +4176,7 @@ void RasterizerStorageGLES3::multimesh_instance_set_transform(RID p_multimesh, i ERR_FAIL_COND(multimesh->transform_format == VS::MULTIMESH_TRANSFORM_2D); int stride = multimesh->color_floats + multimesh->xform_floats + multimesh->custom_data_floats; - float *dataptr = &multimesh->data[stride * p_index]; + float *dataptr = &multimesh->data.write[stride * p_index]; dataptr[0] = p_transform.basis.elements[0][0]; dataptr[1] = p_transform.basis.elements[0][1]; @@ -4025,7 +4207,7 @@ void RasterizerStorageGLES3::multimesh_instance_set_transform_2d(RID p_multimesh ERR_FAIL_COND(multimesh->transform_format == VS::MULTIMESH_TRANSFORM_3D); int stride = multimesh->color_floats + multimesh->xform_floats + multimesh->custom_data_floats; - float *dataptr = &multimesh->data[stride * p_index]; + float *dataptr = &multimesh->data.write[stride * p_index]; dataptr[0] = p_transform.elements[0][0]; dataptr[1] = p_transform.elements[1][0]; @@ -4051,7 +4233,7 @@ void RasterizerStorageGLES3::multimesh_instance_set_color(RID p_multimesh, int p ERR_FAIL_COND(multimesh->color_format == VS::MULTIMESH_COLOR_NONE); int stride = multimesh->color_floats + multimesh->xform_floats + multimesh->custom_data_floats; - float *dataptr = &multimesh->data[stride * p_index + multimesh->xform_floats]; + float *dataptr = &multimesh->data.write[stride * p_index + multimesh->xform_floats]; if (multimesh->color_format == VS::MULTIMESH_COLOR_8BIT) { @@ -4084,7 +4266,7 @@ void RasterizerStorageGLES3::multimesh_instance_set_custom_data(RID p_multimesh, ERR_FAIL_COND(multimesh->custom_data_format == VS::MULTIMESH_CUSTOM_DATA_NONE); int stride = multimesh->color_floats + multimesh->xform_floats + multimesh->custom_data_floats; - float *dataptr = &multimesh->data[stride * p_index + multimesh->xform_floats + multimesh->color_floats]; + float *dataptr = &multimesh->data.write[stride * p_index + multimesh->xform_floats + multimesh->color_floats]; if (multimesh->custom_data_format == VS::MULTIMESH_CUSTOM_DATA_8BIT) { @@ -4124,7 +4306,7 @@ Transform RasterizerStorageGLES3::multimesh_instance_get_transform(RID p_multime ERR_FAIL_COND_V(multimesh->transform_format == VS::MULTIMESH_TRANSFORM_2D, Transform()); int stride = multimesh->color_floats + multimesh->xform_floats + multimesh->custom_data_floats; - float *dataptr = &multimesh->data[stride * p_index]; + float *dataptr = &multimesh->data.write[stride * p_index]; Transform xform; @@ -4151,7 +4333,7 @@ Transform2D RasterizerStorageGLES3::multimesh_instance_get_transform_2d(RID p_mu ERR_FAIL_COND_V(multimesh->transform_format == VS::MULTIMESH_TRANSFORM_3D, Transform2D()); int stride = multimesh->color_floats + multimesh->xform_floats + multimesh->custom_data_floats; - float *dataptr = &multimesh->data[stride * p_index]; + float *dataptr = &multimesh->data.write[stride * p_index]; Transform2D xform; @@ -4173,7 +4355,7 @@ Color RasterizerStorageGLES3::multimesh_instance_get_color(RID p_multimesh, int ERR_FAIL_COND_V(multimesh->color_format == VS::MULTIMESH_COLOR_NONE, Color()); int stride = multimesh->color_floats + multimesh->xform_floats + multimesh->custom_data_floats; - float *dataptr = &multimesh->data[stride * p_index + multimesh->xform_floats]; + float *dataptr = &multimesh->data.write[stride * p_index + multimesh->xform_floats]; if (multimesh->color_format == VS::MULTIMESH_COLOR_8BIT) { union { @@ -4206,7 +4388,7 @@ Color RasterizerStorageGLES3::multimesh_instance_get_custom_data(RID p_multimesh ERR_FAIL_COND_V(multimesh->custom_data_format == VS::MULTIMESH_CUSTOM_DATA_NONE, Color()); int stride = multimesh->color_floats + multimesh->xform_floats + multimesh->custom_data_floats; - float *dataptr = &multimesh->data[stride * p_index + multimesh->xform_floats + multimesh->color_floats]; + float *dataptr = &multimesh->data.write[stride * p_index + multimesh->xform_floats + multimesh->color_floats]; if (multimesh->custom_data_format == VS::MULTIMESH_CUSTOM_DATA_8BIT) { union { @@ -5762,7 +5944,7 @@ void RasterizerStorageGLES3::particles_set_draw_pass_mesh(RID p_particles, int p Particles *particles = particles_owner.getornull(p_particles); ERR_FAIL_COND(!particles); ERR_FAIL_INDEX(p_pass, particles->draw_passes.size()); - particles->draw_passes[p_pass] = p_mesh; + particles->draw_passes.write[p_pass] = p_mesh; } void RasterizerStorageGLES3::particles_restart(RID p_particles) { @@ -6636,7 +6818,7 @@ void RasterizerStorageGLES3::_render_target_allocate(RenderTarget *rt) { for (int j = 0; j < rt->effects.mip_maps[i].sizes.size(); j++) { - RenderTarget::Effects::MipMaps::Size &mm = rt->effects.mip_maps[i].sizes[j]; + RenderTarget::Effects::MipMaps::Size &mm = rt->effects.mip_maps[i].sizes.write[j]; glGenFramebuffers(1, &mm.fbo); glBindFramebuffer(GL_FRAMEBUFFER, mm.fbo); @@ -6989,6 +7171,7 @@ bool RasterizerStorageGLES3::free(RID p_rid) { info.texture_mem -= texture->total_data_size; texture_owner.free(p_rid); memdelete(texture); + } else if (sky_owner.owns(p_rid)) { // delete the sky Sky *sky = sky_owner.get(p_rid); @@ -7048,7 +7231,7 @@ bool RasterizerStorageGLES3::free(RID p_rid) { for (int i = 0; i < ins->materials.size(); i++) { if (ins->materials[i] == p_rid) { - ins->materials[i] = RID(); + ins->materials.write[i] = RID(); } } } @@ -7198,6 +7381,9 @@ bool RasterizerStorageGLES3::free(RID p_rid) { bool RasterizerStorageGLES3::has_os_feature(const String &p_feature) const { + if (p_feature == "bptc") + return config.bptc_supported; + if (p_feature == "s3tc") return config.s3tc_supported; @@ -7398,6 +7584,15 @@ void RasterizerStorageGLES3::initialize() { glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 8, 8, 0, GL_RGB, GL_UNSIGNED_BYTE, anisotexdata); glGenerateMipmap(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, 0); + + glGenTextures(1, &resources.white_tex_3d); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_3D, resources.white_tex_3d); + glTexImage3D(GL_TEXTURE_3D, 0, GL_RGB, 2, 2, 2, 0, GL_RGB, GL_UNSIGNED_BYTE, whitetexdata); + + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_BASE_LEVEL, 0); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAX_LEVEL, 0); } glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &config.max_texture_image_units); @@ -7456,7 +7651,7 @@ void RasterizerStorageGLES3::initialize() { { //transform feedback buffers - uint32_t xf_feedback_size = GLOBAL_DEF("rendering/limits/buffers/blend_shape_max_buffer_size_kb", 4096); + uint32_t xf_feedback_size = GLOBAL_DEF_RST("rendering/limits/buffers/blend_shape_max_buffer_size_kb", 4096); for (int i = 0; i < 2; i++) { glGenBuffers(1, &resources.transform_feedback_buffers[i]); @@ -7479,7 +7674,6 @@ void RasterizerStorageGLES3::initialize() { #endif frame.count = 0; - frame.prev_tick = 0; frame.delta = 0; frame.current_rt = NULL; config.keep_original_textures = false; diff --git a/drivers/gles3/rasterizer_storage_gles3.h b/drivers/gles3/rasterizer_storage_gles3.h index 7a2d56f69b..b74dd77e26 100644 --- a/drivers/gles3/rasterizer_storage_gles3.h +++ b/drivers/gles3/rasterizer_storage_gles3.h @@ -123,6 +123,8 @@ public: GLuint normal_tex; GLuint aniso_tex; + GLuint white_tex_3d; + GLuint quadie; GLuint quadie_array; @@ -248,9 +250,10 @@ public: String path; uint32_t flags; - int width, height; - int alloc_width, alloc_height; + int width, height, depth; + int alloc_width, alloc_height, alloc_depth; Image::Format format; + VS::TextureType type; GLenum target; GLenum gl_format_cache; @@ -268,12 +271,13 @@ public: GLuint tex_id; bool using_srgb; + bool redraw_if_visible; uint16_t stored_cube_sides; RenderTarget *render_target; - Ref<Image> images[6]; + Vector<Ref<Image> > images; VisualServer::TextureDetectCallback detect_3d; void *detect_3d_ud; @@ -306,6 +310,7 @@ public: detect_normal = NULL; detect_normal_ud = NULL; proxy = NULL; + redraw_if_visible = false; } _ALWAYS_INLINE_ Texture *get_ptr() { @@ -335,20 +340,22 @@ public: mutable RID_Owner<Texture> texture_owner; - Ref<Image> _get_gl_image_and_format(const Ref<Image> &p_image, Image::Format p_format, uint32_t p_flags, GLenum &r_gl_format, GLenum &r_gl_internal_format, GLenum &r_gl_type, bool &r_compressed, bool &srgb); + Ref<Image> _get_gl_image_and_format(const Ref<Image> &p_image, Image::Format p_format, uint32_t p_flags, Image::Format &r_real_format, GLenum &r_gl_format, GLenum &r_gl_internal_format, GLenum &r_gl_type, bool &r_compressed, bool &srgb) const; virtual RID texture_create(); - virtual void texture_allocate(RID p_texture, int p_width, int p_height, Image::Format p_format, uint32_t p_flags = VS::TEXTURE_FLAGS_DEFAULT); - virtual void texture_set_data(RID p_texture, const Ref<Image> &p_image, VS::CubeMapSide p_cube_side = VS::CUBEMAP_LEFT); - virtual void texture_set_data_partial(RID p_texture, const Ref<Image> &p_image, int src_x, int src_y, int src_w, int src_h, int dst_x, int dst_y, int p_dst_mip, VS::CubeMapSide p_cube_side = VS::CUBEMAP_LEFT); - virtual Ref<Image> texture_get_data(RID p_texture, VS::CubeMapSide p_cube_side = VS::CUBEMAP_LEFT) const; + virtual void texture_allocate(RID p_texture, int p_width, int p_height, int p_depth_3d, Image::Format p_format, VS::TextureType p_type, uint32_t p_flags = VS::TEXTURE_FLAGS_DEFAULT); + virtual void texture_set_data(RID p_texture, const Ref<Image> &p_image, int p_layer = 0); + virtual void texture_set_data_partial(RID p_texture, const Ref<Image> &p_image, int src_x, int src_y, int src_w, int src_h, int dst_x, int dst_y, int p_dst_mip, int p_layer = 0); + virtual Ref<Image> texture_get_data(RID p_texture, int p_layer = 0) const; virtual void texture_set_flags(RID p_texture, uint32_t p_flags); virtual uint32_t texture_get_flags(RID p_texture) const; virtual Image::Format texture_get_format(RID p_texture) const; + virtual VS::TextureType texture_get_type(RID p_texture) const; virtual uint32_t texture_get_texid(RID p_texture) const; virtual uint32_t texture_get_width(RID p_texture) const; virtual uint32_t texture_get_height(RID p_texture) const; - virtual void texture_set_size_override(RID p_texture, int p_width, int p_height); + virtual uint32_t texture_get_depth(RID p_texture) const; + virtual void texture_set_size_override(RID p_texture, int p_width, int p_height, int p_depth); virtual void texture_set_path(RID p_texture, const String &p_path); virtual String texture_get_path(RID p_texture) const; @@ -366,6 +373,7 @@ public: virtual void texture_set_detect_normal_callback(RID p_texture, VisualServer::TextureDetectCallback p_callback, void *p_userdata); virtual void texture_set_proxy(RID p_texture, RID p_proxy); + virtual void texture_set_force_redraw_if_visible(RID p_texture, bool p_enable); /* SKY API */ @@ -407,6 +415,7 @@ public: Map<StringName, RID> default_textures; + Vector<ShaderLanguage::DataType> texture_types; Vector<ShaderLanguage::ShaderNode::Uniform::Hint> texture_hints; bool valid; @@ -529,6 +538,7 @@ public: Map<StringName, Variant> params; SelfList<Material> list; SelfList<Material> dirty_list; + Vector<bool> texture_is_3d; Vector<RID> textures; float line_width; int render_priority; @@ -690,7 +700,6 @@ public: AABB custom_aabb; mutable uint64_t last_pass; SelfList<MultiMesh>::List multimeshes; - _FORCE_INLINE_ void update_multimeshes() { SelfList<MultiMesh> *mm = multimeshes.first(); @@ -1425,7 +1434,6 @@ public: int canvas_draw_commands; float time[4]; float delta; - uint64_t prev_tick; uint64_t count; } frame; diff --git a/drivers/gles3/shader_compiler_gles3.cpp b/drivers/gles3/shader_compiler_gles3.cpp index 9ad16ac2a2..0c353d42bb 100644 --- a/drivers/gles3/shader_compiler_gles3.cpp +++ b/drivers/gles3/shader_compiler_gles3.cpp @@ -341,6 +341,7 @@ String ShaderCompilerGLES3::_dump_node_code(SL::Node *p_node, int p_level, Gener r_gen_code.texture_uniforms.resize(max_texture_uniforms); r_gen_code.texture_hints.resize(max_texture_uniforms); + r_gen_code.texture_types.resize(max_texture_uniforms); Vector<int> uniform_sizes; Vector<int> uniform_alignments; @@ -365,17 +366,18 @@ String ShaderCompilerGLES3::_dump_node_code(SL::Node *p_node, int p_level, Gener if (SL::is_sampler_type(E->get().type)) { r_gen_code.vertex_global += ucode; r_gen_code.fragment_global += ucode; - r_gen_code.texture_uniforms[E->get().texture_order] = _mkid(E->key()); - r_gen_code.texture_hints[E->get().texture_order] = E->get().hint; + r_gen_code.texture_uniforms.write[E->get().texture_order] = _mkid(E->key()); + r_gen_code.texture_hints.write[E->get().texture_order] = E->get().hint; + r_gen_code.texture_types.write[E->get().texture_order] = E->get().type; } else { if (!uses_uniforms) { r_gen_code.defines.push_back(String("#define USE_MATERIAL\n").ascii()); uses_uniforms = true; } - uniform_defines[E->get().order] = ucode; - uniform_sizes[E->get().order] = _get_datatype_size(E->get().type); - uniform_alignments[E->get().order] = _get_datatype_alignment(E->get().type); + uniform_defines.write[E->get().order] = ucode; + uniform_sizes.write[E->get().order] = _get_datatype_size(E->get().type); + uniform_alignments.write[E->get().order] = _get_datatype_alignment(E->get().type); } p_actions.uniforms->insert(E->key(), E->get()); @@ -700,6 +702,11 @@ String ShaderCompilerGLES3::_dump_node_code(SL::Node *p_node, int p_level, Gener } } else if (cfnode->flow_op == SL::FLOW_OP_DISCARD) { + if (p_actions.usage_flag_pointers.has("DISCARD") && !used_flag_pointers.has("DISCARD")) { + *p_actions.usage_flag_pointers["DISCARD"] = true; + used_flag_pointers.insert("DISCARD"); + } + code = "discard;"; } else if (cfnode->flow_op == SL::FLOW_OP_CONTINUE) { @@ -780,8 +787,6 @@ ShaderCompilerGLES3::ShaderCompilerGLES3() { actions[VS::SHADER_CANVAS_ITEM].renames["NORMAL"] = "normal"; actions[VS::SHADER_CANVAS_ITEM].renames["NORMALMAP"] = "normal_map"; actions[VS::SHADER_CANVAS_ITEM].renames["NORMALMAP_DEPTH"] = "normal_depth"; - actions[VS::SHADER_CANVAS_ITEM].renames["UV"] = "uv_interp"; - actions[VS::SHADER_CANVAS_ITEM].renames["COLOR"] = "color"; actions[VS::SHADER_CANVAS_ITEM].renames["TEXTURE"] = "color_texture"; actions[VS::SHADER_CANVAS_ITEM].renames["TEXTURE_PIXEL_SIZE"] = "color_texpixel_size"; actions[VS::SHADER_CANVAS_ITEM].renames["NORMAL_TEXTURE"] = "normal_texture"; @@ -824,7 +829,7 @@ ShaderCompilerGLES3::ShaderCompilerGLES3() { actions[VS::SHADER_SPATIAL].renames["UV2"] = "uv2_interp"; actions[VS::SHADER_SPATIAL].renames["COLOR"] = "color_interp"; actions[VS::SHADER_SPATIAL].renames["POINT_SIZE"] = "gl_PointSize"; - //actions[VS::SHADER_SPATIAL].renames["INSTANCE_ID"]=ShaderLanguage::TYPE_INT; + actions[VS::SHADER_SPATIAL].renames["INSTANCE_ID"] = "gl_InstanceID"; //builtins @@ -846,13 +851,11 @@ ShaderCompilerGLES3::ShaderCompilerGLES3() { actions[VS::SHADER_SPATIAL].renames["CLEARCOAT_GLOSS"] = "clearcoat_gloss"; actions[VS::SHADER_SPATIAL].renames["ANISOTROPY"] = "anisotropy"; actions[VS::SHADER_SPATIAL].renames["ANISOTROPY_FLOW"] = "anisotropy_flow"; - //actions[VS::SHADER_SPATIAL].renames["SSS_SPREAD"] = "sss_spread"; actions[VS::SHADER_SPATIAL].renames["SSS_STRENGTH"] = "sss_strength"; actions[VS::SHADER_SPATIAL].renames["TRANSMISSION"] = "transmission"; actions[VS::SHADER_SPATIAL].renames["AO"] = "ao"; actions[VS::SHADER_SPATIAL].renames["AO_LIGHT_AFFECT"] = "ao_light_affect"; actions[VS::SHADER_SPATIAL].renames["EMISSION"] = "emission"; - //actions[VS::SHADER_SPATIAL].renames["SCREEN_UV"]=ShaderLanguage::TYPE_VEC2; actions[VS::SHADER_SPATIAL].renames["POINT_COORD"] = "gl_PointCoord"; actions[VS::SHADER_SPATIAL].renames["INSTANCE_CUSTOM"] = "instance_custom"; actions[VS::SHADER_SPATIAL].renames["SCREEN_UV"] = "screen_uv"; @@ -894,8 +897,6 @@ ShaderCompilerGLES3::ShaderCompilerGLES3() { actions[VS::SHADER_SPATIAL].usage_defines["DIFFUSE_LIGHT"] = "#define USE_LIGHT_SHADER_CODE\n"; actions[VS::SHADER_SPATIAL].usage_defines["SPECULAR_LIGHT"] = "#define USE_LIGHT_SHADER_CODE\n"; - actions[VS::SHADER_SPATIAL].renames["SSS_STRENGTH"] = "sss_strength"; - actions[VS::SHADER_SPATIAL].render_mode_defines["skip_vertex_transform"] = "#define SKIP_TRANSFORM_USED\n"; actions[VS::SHADER_SPATIAL].render_mode_defines["world_vertex_coords"] = "#define VERTEX_WORLD_COORDS_USED\n"; actions[VS::SHADER_SPATIAL].render_mode_defines["ensure_correct_normals"] = "#define ENSURE_CORRECT_NORMALS\n"; @@ -913,6 +914,7 @@ ShaderCompilerGLES3::ShaderCompilerGLES3() { actions[VS::SHADER_SPATIAL].render_mode_defines["specular_toon"] = "#define SPECULAR_TOON\n"; actions[VS::SHADER_SPATIAL].render_mode_defines["specular_disabled"] = "#define SPECULAR_DISABLED\n"; actions[VS::SHADER_SPATIAL].render_mode_defines["shadows_disabled"] = "#define SHADOWS_DISABLED\n"; + actions[VS::SHADER_SPATIAL].render_mode_defines["ambient_light_disabled"] = "#define AMBIENT_LIGHT_DISABLED\n"; /* PARTICLES SHADER */ diff --git a/drivers/gles3/shader_compiler_gles3.h b/drivers/gles3/shader_compiler_gles3.h index bf776ee062..7a32057741 100644 --- a/drivers/gles3/shader_compiler_gles3.h +++ b/drivers/gles3/shader_compiler_gles3.h @@ -52,6 +52,7 @@ public: Vector<CharString> defines; Vector<StringName> texture_uniforms; + Vector<ShaderLanguage::DataType> texture_types; Vector<ShaderLanguage::ShaderNode::Uniform::Hint> texture_hints; Vector<uint32_t> uniform_offsets; diff --git a/drivers/gles3/shader_gles3.cpp b/drivers/gles3/shader_gles3.cpp index 08b8a1cc26..ca0ce5cd3e 100644 --- a/drivers/gles3/shader_gles3.cpp +++ b/drivers/gles3/shader_gles3.cpp @@ -554,7 +554,7 @@ ShaderGLES3::Version *ShaderGLES3::get_current_version() { v.texture_uniform_locations.resize(cc->texture_uniforms.size()); for (int i = 0; i < cc->texture_uniforms.size(); i++) { - v.texture_uniform_locations[i] = glGetUniformLocation(v.id, String(cc->texture_uniforms[i]).ascii().get_data()); + v.texture_uniform_locations.write[i] = glGetUniformLocation(v.id, String(cc->texture_uniforms[i]).ascii().get_data()); glUniform1i(v.texture_uniform_locations[i], i + base_material_tex_index); } } diff --git a/drivers/gles3/shaders/scene.glsl b/drivers/gles3/shaders/scene.glsl index ed8df04377..2d6f42679f 100644 --- a/drivers/gles3/shaders/scene.glsl +++ b/drivers/gles3/shaders/scene.glsl @@ -91,6 +91,7 @@ layout(std140) uniform SceneData { //ubo:0 mediump float subsurface_scatter_width; mediump float ambient_occlusion_affect_light; mediump float ambient_occlusion_affect_ao_channel; + mediump float opaque_prepass_threshold; bool fog_depth_enabled; highp float fog_depth_begin; @@ -338,7 +339,7 @@ void main() { #endif #endif - float roughness=0.0; + float roughness = 1.0; //defines that make writing custom shaders easier #define projection_matrix local_projection @@ -571,11 +572,6 @@ in vec3 normal_interp; /* PBR CHANNELS */ -//used on forward mainly -uniform bool no_ambient_light; - - - #ifdef USE_RADIANCE_MAP @@ -684,6 +680,7 @@ layout(std140) uniform SceneData { mediump float subsurface_scatter_width; mediump float ambient_occlusion_affect_light; mediump float ambient_occlusion_affect_ao_channel; + mediump float opaque_prepass_threshold; bool fog_depth_enabled; highp float fog_depth_begin; @@ -1031,12 +1028,11 @@ LIGHT_SHADER_CODE diffuse_brdf_NL = cNdotL * (1.0 / M_PI); #endif -#if defined(TRANSMISSION_USED) - diffuse_light += light_color * diffuse_color * mix(vec3(diffuse_brdf_NL), vec3(M_PI), transmission) * attenuation; -#else diffuse_light += light_color * diffuse_color * diffuse_brdf_NL * attenuation; -#endif +#if defined(TRANSMISSION_USED) + diffuse_light += light_color * diffuse_color * (vec3(1.0 / M_PI) - diffuse_brdf_NL) * transmission * attenuation; +#endif #if defined(LIGHT_USE_RIM) @@ -1612,18 +1608,18 @@ void main() { //lay out everything, whathever is unused is optimized away anyway highp vec3 vertex = vertex_interp; - vec3 albedo = vec3(0.8,0.8,0.8); + vec3 albedo = vec3(1.0); vec3 transmission = vec3(0.0); float metallic = 0.0; float specular = 0.5; - vec3 emission = vec3(0.0,0.0,0.0); + vec3 emission = vec3(0.0); float roughness = 1.0; float rim = 0.0; float rim_tint = 0.0; - float clearcoat=0.0; - float clearcoat_gloss=0.0; - float anisotropy = 1.0; - vec2 anisotropy_flow = vec2(1.0,0.0); + float clearcoat = 0.0; + float clearcoat_gloss = 0.0; + float anisotropy = 0.0; + vec2 anisotropy_flow = vec2(1.0, 0.0); #if defined(ENABLE_AO) float ao=1.0; @@ -1633,7 +1629,7 @@ void main() { float alpha = 1.0; #if defined(DO_SIDE_CHECK) - float side=float(gl_FrontFacing)*2.0-1.0; + float side=gl_FrontFacing ? 1.0 : -1.0; #else float side=1.0; #endif @@ -1695,15 +1691,16 @@ FRAGMENT_SHADER_CODE #ifdef USE_OPAQUE_PREPASS - if (alpha<0.99) { + if (alpha<opaque_prepass_threshold) { discard; } + #endif #if defined(ENABLE_NORMALMAP) normalmap.xy=normalmap.xy*2.0-1.0; - normalmap.z=sqrt(1.0-dot(normalmap.xy,normalmap.xy)); //always ignore Z, as it can be RG packed, Z may be pos/neg, etc. + normalmap.z=sqrt(max(0.0, 1.0-dot(normalmap.xy,normalmap.xy))); //always ignore Z, as it can be RG packed, Z may be pos/neg, etc. normal = normalize( mix(normal_interp,tangent * normalmap.x + binormal * normalmap.y + normal * normalmap.z,normaldepth) ) * side; @@ -1752,42 +1749,43 @@ FRAGMENT_SHADER_CODE #ifdef USE_RADIANCE_MAP - if (no_ambient_light) { - ambient_light=vec3(0.0,0.0,0.0); - } else { - { - - { //read radiance from dual paraboloid +#ifdef AMBIENT_LIGHT_DISABLED + ambient_light=vec3(0.0,0.0,0.0); +#else + { - vec3 ref_vec = reflect(-eye_vec,normal); //2.0 * ndotv * normal - view; // reflect(v, n); - ref_vec=normalize((radiance_inverse_xform * vec4(ref_vec,0.0)).xyz); - vec3 radiance = textureDualParaboloid(radiance_map,ref_vec,roughness) * bg_energy; - env_reflection_light = radiance; + { //read radiance from dual paraboloid - } - //no longer a cubemap - //vec3 radiance = textureLod(radiance_cube, r, lod).xyz * ( brdf.x + brdf.y); + vec3 ref_vec = reflect(-eye_vec,normal); //2.0 * ndotv * normal - view; // reflect(v, n); + ref_vec=normalize((radiance_inverse_xform * vec4(ref_vec,0.0)).xyz); + vec3 radiance = textureDualParaboloid(radiance_map,ref_vec,roughness) * bg_energy; + env_reflection_light = radiance; } + //no longer a cubemap + //vec3 radiance = textureLod(radiance_cube, r, lod).xyz * ( brdf.x + brdf.y); + + } #ifndef USE_LIGHTMAP - { + { - vec3 ambient_dir=normalize((radiance_inverse_xform * vec4(normal,0.0)).xyz); - vec3 env_ambient=textureDualParaboloid(radiance_map,ambient_dir,1.0) * bg_energy; + vec3 ambient_dir=normalize((radiance_inverse_xform * vec4(normal,0.0)).xyz); + vec3 env_ambient=textureDualParaboloid(radiance_map,ambient_dir,1.0) * bg_energy; - ambient_light=mix(ambient_light_color.rgb,env_ambient,radiance_ambient_contribution); - //ambient_light=vec3(0.0,0.0,0.0); - } -#endif + ambient_light=mix(ambient_light_color.rgb,env_ambient,radiance_ambient_contribution); + //ambient_light=vec3(0.0,0.0,0.0); } +#endif +#endif //AMBIENT_LIGHT_DISABLED #else - if (no_ambient_light){ - ambient_light=vec3(0.0,0.0,0.0); - } else { - ambient_light=ambient_light_color.rgb; - } +#ifdef AMBIENT_LIGHT_DISABLED + ambient_light=vec3(0.0,0.0,0.0); +#else + ambient_light=ambient_light_color.rgb; +#endif //AMBIENT_LIGHT_DISABLED + #endif ambient_light*=ambient_energy; @@ -2142,6 +2140,8 @@ FRAGMENT_SHADER_CODE #else + + //approximate ambient scale for SSAO, since we will lack full ambient float max_emission=max(emission.r,max(emission.g,emission.b)); float max_ambient=max(ambient_light.r,max(ambient_light.g,ambient_light.b)); @@ -2173,7 +2173,6 @@ FRAGMENT_SHADER_CODE frag_color=vec4(emission+ambient_light+diffuse_light+specular_light,alpha); #endif //SHADELESS - #endif //USE_MULTIPLE_RENDER_TARGETS diff --git a/drivers/gles3/shaders/tonemap.glsl b/drivers/gles3/shaders/tonemap.glsl index a75871f08e..63475c9039 100644 --- a/drivers/gles3/shaders/tonemap.glsl +++ b/drivers/gles3/shaders/tonemap.glsl @@ -1,28 +1,27 @@ [vertex] - -layout(location=0) in highp vec4 vertex_attrib; -layout(location=4) in vec2 uv_in; +layout (location = 0) in highp vec4 vertex_attrib; +layout (location = 4) in vec2 uv_in; out vec2 uv_interp; -void main() { - +void main() +{ gl_Position = vertex_attrib; + uv_interp = uv_in; -#ifdef V_FLIP - uv_interp.y = 1.0-uv_interp.y; -#endif + #ifdef V_FLIP + uv_interp.y = 1.0f - uv_interp.y; + #endif } [fragment] #if !defined(GLES_OVER_GL) -precision mediump float; + precision mediump float; #endif - in vec2 uv_interp; uniform highp sampler2D source; //texunit:0 @@ -31,297 +30,286 @@ uniform float exposure; uniform float white; #ifdef USE_AUTO_EXPOSURE - -uniform highp sampler2D source_auto_exposure; //texunit:1 -uniform highp float auto_exposure_grey; - + uniform highp sampler2D source_auto_exposure; //texunit:1 + uniform highp float auto_exposure_grey; #endif #if defined(USE_GLOW_LEVEL1) || defined(USE_GLOW_LEVEL2) || defined(USE_GLOW_LEVEL3) || defined(USE_GLOW_LEVEL4) || defined(USE_GLOW_LEVEL5) || defined(USE_GLOW_LEVEL6) || defined(USE_GLOW_LEVEL7) + #define USING_GLOW // only use glow when at least one glow level is selected -uniform highp sampler2D source_glow; //texunit:2 -uniform highp float glow_intensity; - + uniform highp sampler2D source_glow; //texunit:2 + uniform highp float glow_intensity; #endif #ifdef USE_BCS - -uniform vec3 bcs; - + uniform vec3 bcs; #endif #ifdef USE_COLOR_CORRECTION - -uniform sampler2D color_correction; //texunit:3 - + uniform sampler2D color_correction; //texunit:3 #endif - -layout(location = 0) out vec4 frag_color; +layout (location = 0) out vec4 frag_color; #ifdef USE_GLOW_FILTER_BICUBIC + // w0, w1, w2, and w3 are the four cubic B-spline basis functions + float w0(float a) + { + return (1.0f / 6.0f) * (a * (a * (-a + 3.0f) - 3.0f) + 1.0f); + } -// w0, w1, w2, and w3 are the four cubic B-spline basis functions -float w0(float a) -{ - return (1.0/6.0)*(a*(a*(-a + 3.0) - 3.0) + 1.0); -} - -float w1(float a) -{ - return (1.0/6.0)*(a*a*(3.0*a - 6.0) + 4.0); -} - -float w2(float a) -{ - return (1.0/6.0)*(a*(a*(-3.0*a + 3.0) + 3.0) + 1.0); -} - -float w3(float a) -{ - return (1.0/6.0)*(a*a*a); -} - -// g0 and g1 are the two amplitude functions -float g0(float a) -{ - return w0(a) + w1(a); -} + float w1(float a) + { + return (1.0f / 6.0f) * (a * a * (3.0f * a - 6.0f) + 4.0f); + } -float g1(float a) -{ - return w2(a) + w3(a); -} + float w2(float a) + { + return (1.0f / 6.0f) * (a * (a * (-3.0f * a + 3.0f) + 3.0f) + 1.0f); + } -// h0 and h1 are the two offset functions -float h0(float a) -{ - return -1.0 + w1(a) / (w0(a) + w1(a)); -} + float w3(float a) + { + return (1.0f / 6.0f) * (a * a * a); + } -float h1(float a) -{ - return 1.0 + w3(a) / (w2(a) + w3(a)); -} + // g0 and g1 are the two amplitude functions + float g0(float a) + { + return w0(a) + w1(a); + } -uniform ivec2 glow_texture_size; + float g1(float a) + { + return w2(a) + w3(a); + } -vec4 texture2D_bicubic(sampler2D tex, vec2 uv,int p_lod) -{ - float lod=float(p_lod); - vec2 tex_size = vec2(glow_texture_size >> p_lod); - vec2 pixel_size =1.0/tex_size; - uv = uv*tex_size + 0.5; - vec2 iuv = floor( uv ); - vec2 fuv = fract( uv ); - - float g0x = g0(fuv.x); - float g1x = g1(fuv.x); - float h0x = h0(fuv.x); - float h1x = h1(fuv.x); - float h0y = h0(fuv.y); - float h1y = h1(fuv.y); - - vec2 p0 = (vec2(iuv.x + h0x, iuv.y + h0y) - 0.5) * pixel_size; - vec2 p1 = (vec2(iuv.x + h1x, iuv.y + h0y) - 0.5) * pixel_size; - vec2 p2 = (vec2(iuv.x + h0x, iuv.y + h1y) - 0.5) * pixel_size; - vec2 p3 = (vec2(iuv.x + h1x, iuv.y + h1y) - 0.5) * pixel_size; - - return g0(fuv.y) * (g0x * textureLod(tex, p0,lod) + - g1x * textureLod(tex, p1,lod)) + - g1(fuv.y) * (g0x * textureLod(tex, p2,lod) + - g1x * textureLod(tex, p3,lod)); -} + // h0 and h1 are the two offset functions + float h0(float a) + { + return -1.0f + w1(a) / (w0(a) + w1(a)); + } + float h1(float a) + { + return 1.0f + w3(a) / (w2(a) + w3(a)); + } + uniform ivec2 glow_texture_size; -#define GLOW_TEXTURE_SAMPLE(m_tex,m_uv,m_lod) texture2D_bicubic(m_tex,m_uv,m_lod) + vec4 texture2D_bicubic(sampler2D tex, vec2 uv, int p_lod) + { + float lod = float(p_lod); + vec2 tex_size = vec2(glow_texture_size >> p_lod); + vec2 pixel_size = vec2(1.0f) / tex_size; + + uv = uv * tex_size + vec2(0.5f); + + vec2 iuv = floor(uv); + vec2 fuv = fract(uv); + + float g0x = g0(fuv.x); + float g1x = g1(fuv.x); + float h0x = h0(fuv.x); + float h1x = h1(fuv.x); + float h0y = h0(fuv.y); + float h1y = h1(fuv.y); + + vec2 p0 = (vec2(iuv.x + h0x, iuv.y + h0y) - vec2(0.5f)) * pixel_size; + vec2 p1 = (vec2(iuv.x + h1x, iuv.y + h0y) - vec2(0.5f)) * pixel_size; + vec2 p2 = (vec2(iuv.x + h0x, iuv.y + h1y) - vec2(0.5f)) * pixel_size; + vec2 p3 = (vec2(iuv.x + h1x, iuv.y + h1y) - vec2(0.5f)) * pixel_size; + + return g0(fuv.y) * (g0x * textureLod(tex, p0,lod) + + g1x * textureLod(tex, p1,lod)) + + g1(fuv.y) * (g0x * textureLod(tex, p2,lod) + + g1x * textureLod(tex, p3,lod)); + } + #define GLOW_TEXTURE_SAMPLE(m_tex, m_uv, m_lod) texture2D_bicubic(m_tex, m_uv, m_lod) #else - -#define GLOW_TEXTURE_SAMPLE(m_tex,m_uv,m_lod) textureLod(m_tex,m_uv,float(m_lod)) - + #define GLOW_TEXTURE_SAMPLE(m_tex, m_uv, m_lod) textureLod(m_tex, m_uv, float(m_lod)) #endif +vec3 tonemap_filmic(vec3 color, float white) +{ + const float A = 0.15f; + const float B = 0.50f; + const float C = 0.10f; + const float D = 0.20f; + const float E = 0.02f; + const float F = 0.30f; + const float W = 11.2f; + + vec3 color_tonemapped = ((color * (A * color + C * B) + D * E) / (color * (A * color + B) + D * F)) - E / F; + float white_tonemapped = ((white * (A * white + C * B) + D * E) / (white * (A * white + B) + D * F)) - E / F; + + return clamp(color_tonemapped / white_tonemapped, vec3(0.0f), vec3(1.0f)); +} -vec3 tonemap_filmic(vec3 color,float white) { - - float A = 0.15; - float B = 0.50; - float C = 0.10; - float D = 0.20; - float E = 0.02; - float F = 0.30; - float W = 11.2; - - vec3 coltn = ((color*(A*color+C*B)+D*E)/(color*(A*color+B)+D*F))-E/F; - float whitetn = ((white*(A*white+C*B)+D*E)/(white*(A*white+B)+D*F))-E/F; +vec3 tonemap_aces(vec3 color, float white) +{ + const float A = 2.51f; + const float B = 0.03f; + const float C = 2.43f; + const float D = 0.59f; + const float E = 0.14f; - return coltn/whitetn; + vec3 color_tonemapped = (color * (A * color + B)) / (color * (C * color + D) + E); + float white_tonemapped = (white * (A * white + B)) / (white * (C * white + D) + E); + return clamp(color_tonemapped / white_tonemapped, vec3(0.0f), vec3(1.0f)); } -vec3 tonemap_aces(vec3 color) { - float a = 2.51f; - float b = 0.03f; - float c = 2.43f; - float d = 0.59f; - float e = 0.14f; - return color = clamp((color*(a*color+b))/(color*(c*color+d)+e),vec3(0.0),vec3(1.0)); +vec3 tonemap_reindhart(vec3 color, float white) +{ + return clamp((color) / (1.0f + color) * (1.0f + (color / (white))), vec3(0.0f), vec3(1.0f)); // whitepoint is probably not in linear space here! } -vec3 tonemap_reindhart(vec3 color,float white) { - - return ( color * ( 1.0 + ( color / ( white) ) ) ) / ( 1.0 + color ); +vec3 linear_to_srgb(vec3 color) // convert linear rgb to srgb, assumes clamped input in range [0;1] +{ + const vec3 a = vec3(0.055f); + return mix((vec3(1.0f) + a) * pow(color.rgb, vec3(1.0f / 2.4f)) - a, 12.92f * color.rgb, lessThan(color.rgb, vec3(0.0031308f))); } -void main() { - - vec4 color = textureLod(source, uv_interp, 0.0); - -#ifdef USE_AUTO_EXPOSURE - - color/=texelFetch(source_auto_exposure,ivec2(0,0),0).r/auto_exposure_grey; -#endif - - color*=exposure; - -#if defined(USE_GLOW_LEVEL1) || defined(USE_GLOW_LEVEL2) || defined(USE_GLOW_LEVEL3) || defined(USE_GLOW_LEVEL4) || defined(USE_GLOW_LEVEL5) || defined(USE_GLOW_LEVEL6) || defined(USE_GLOW_LEVEL7) -#define USING_GLOW -#endif - -#if defined(USING_GLOW) - vec3 glow = vec3(0.0); - -#ifdef USE_GLOW_LEVEL1 - - glow+=GLOW_TEXTURE_SAMPLE(source_glow,uv_interp,1).rgb; -#endif - -#ifdef USE_GLOW_LEVEL2 - glow+=GLOW_TEXTURE_SAMPLE(source_glow,uv_interp,2).rgb; -#endif - -#ifdef USE_GLOW_LEVEL3 - glow+=GLOW_TEXTURE_SAMPLE(source_glow,uv_interp,3).rgb; -#endif - -#ifdef USE_GLOW_LEVEL4 - glow+=GLOW_TEXTURE_SAMPLE(source_glow,uv_interp,4).rgb; -#endif - -#ifdef USE_GLOW_LEVEL5 - glow+=GLOW_TEXTURE_SAMPLE(source_glow,uv_interp,5).rgb; -#endif - -#ifdef USE_GLOW_LEVEL6 - glow+=GLOW_TEXTURE_SAMPLE(source_glow,uv_interp,6).rgb; -#endif - -#ifdef USE_GLOW_LEVEL7 - glow+=GLOW_TEXTURE_SAMPLE(source_glow,uv_interp,7).rgb; -#endif - - - glow *= glow_intensity; +vec3 apply_tonemapping(vec3 color, float white) // inputs are LINEAR, always outputs clamped [0;1] color +{ + #ifdef USE_REINDHART_TONEMAPPER + return tonemap_reindhart(color, white); + #endif -#endif + #ifdef USE_FILMIC_TONEMAPPER + return tonemap_filmic(color, white); + #endif + #ifdef USE_ACES_TONEMAPPER + return tonemap_aces(color, white); + #endif -#ifdef USE_REINDHART_TONEMAPPER + return clamp(color, vec3(0.0f), vec3(1.0f)); // no other seleced -> linear +} - color.rgb = tonemap_reindhart(color.rgb,white); +vec3 gather_glow(sampler2D tex, vec2 uv) // sample all selected glow levels +{ + vec3 glow = vec3(0.0f); -# if defined(USING_GLOW) - glow = tonemap_reindhart(glow,white); -# endif + #ifdef USE_GLOW_LEVEL1 + glow += GLOW_TEXTURE_SAMPLE(tex, uv, 1).rgb; + #endif -#endif + #ifdef USE_GLOW_LEVEL2 + glow += GLOW_TEXTURE_SAMPLE(tex, uv, 2).rgb; + #endif -#ifdef USE_FILMIC_TONEMAPPER + #ifdef USE_GLOW_LEVEL3 + glow += GLOW_TEXTURE_SAMPLE(tex, uv, 3).rgb; + #endif - color.rgb = tonemap_filmic(color.rgb,white); + #ifdef USE_GLOW_LEVEL4 + glow += GLOW_TEXTURE_SAMPLE(tex, uv, 4).rgb; + #endif -# if defined(USING_GLOW) - glow = tonemap_filmic(glow,white); -# endif + #ifdef USE_GLOW_LEVEL5 + glow += GLOW_TEXTURE_SAMPLE(tex, uv, 5).rgb; + #endif -#endif + #ifdef USE_GLOW_LEVEL6 + glow += GLOW_TEXTURE_SAMPLE(tex, uv, 6).rgb; + #endif -#ifdef USE_ACES_TONEMAPPER + #ifdef USE_GLOW_LEVEL7 + glow += GLOW_TEXTURE_SAMPLE(tex, uv, 7).rgb; + #endif - color.rgb = tonemap_aces(color.rgb); + return glow; +} -# if defined(USING_GLOW) - glow = tonemap_aces(glow); -# endif +vec3 apply_glow(vec3 color, vec3 glow) // apply glow using the selected blending mode +{ + #ifdef USE_GLOW_REPLACE + color = glow; + #endif -#endif + #ifdef USE_GLOW_SCREEN + color = max((color + glow) - (color * glow), vec3(0.0)); + #endif -#ifdef KEEP_3D_LINEAR - // leave color as is... -#else - //regular Linear -> SRGB conversion - vec3 a = vec3(0.055); - color.rgb = mix( (vec3(1.0)+a)*pow(color.rgb,vec3(1.0/2.4))-a , 12.92*color.rgb , lessThan(color.rgb,vec3(0.0031308))); -#endif + #ifdef USE_GLOW_SOFTLIGHT + glow = glow * vec3(0.5f) + vec3(0.5f); -#if defined(USING_GLOW) - glow = mix( (vec3(1.0)+a)*pow(glow,vec3(1.0/2.4))-a , 12.92*glow , lessThan(glow,vec3(0.0031308))); -#endif + color.r = (glow.r <= 0.5f) ? (color.r - (1.0f - 2.0f * glow.r) * color.r * (1.0f - color.r)) : (((glow.r > 0.5f) && (color.r <= 0.25f)) ? (color.r + (2.0f * glow.r - 1.0f) * (4.0f * color.r * (4.0f * color.r + 1.0f) * (color.r - 1.0f) + 7.0f * color.r)) : (color.r + (2.0f * glow.r - 1.0f) * (sqrt(color.r) - color.r))); + color.g = (glow.g <= 0.5f) ? (color.g - (1.0f - 2.0f * glow.g) * color.g * (1.0f - color.g)) : (((glow.g > 0.5f) && (color.g <= 0.25f)) ? (color.g + (2.0f * glow.g - 1.0f) * (4.0f * color.g * (4.0f * color.g + 1.0f) * (color.g - 1.0f) + 7.0f * color.g)) : (color.g + (2.0f * glow.g - 1.0f) * (sqrt(color.g) - color.g))); + color.b = (glow.b <= 0.5f) ? (color.b - (1.0f - 2.0f * glow.b) * color.b * (1.0f - color.b)) : (((glow.b > 0.5f) && (color.b <= 0.25f)) ? (color.b + (2.0f * glow.b - 1.0f) * (4.0f * color.b * (4.0f * color.b + 1.0f) * (color.b - 1.0f) + 7.0f * color.b)) : (color.b + (2.0f * glow.b - 1.0f) * (sqrt(color.b) - color.b))); + #endif -//glow needs to be added in SRGB space (together with image space effects) + #if !defined(USE_GLOW_SCREEN) && !defined(USE_GLOW_SOFTLIGHT) && !defined(USE_GLOW_REPLACE) // no other selected -> additive + color += glow; + #endif - color.rgb = clamp(color.rgb,0.0,1.0); + return color; +} -#if defined(USING_GLOW) - glow = clamp(glow,0.0,1.0); -#endif +vec3 apply_bcs(vec3 color, vec3 bcs) +{ + color = mix(vec3(0.0f), color, bcs.x); + color = mix(vec3(0.5f), color, bcs.y); + color = mix(vec3(dot(vec3(1.0f), color) * 0.33333f), color, bcs.z); -#ifdef USE_GLOW_REPLACE + return color; +} - color.rgb = glow; +vec3 apply_color_correction(vec3 color, sampler2D correction_tex) +{ + color.r = texture(correction_tex, vec2(color.r, 0.0f)).r; + color.g = texture(correction_tex, vec2(color.g, 0.0f)).g; + color.b = texture(correction_tex, vec2(color.b, 0.0f)).b; -#endif + return color; +} -#ifdef USE_GLOW_SCREEN +void main() +{ + vec3 color = textureLod(source, uv_interp, 0.0f).rgb; - color.rgb = max((color.rgb + glow) - (color.rgb * glow), vec3(0.0)); + // Exposure -#endif + #ifdef USE_AUTO_EXPOSURE + color /= texelFetch(source_auto_exposure, ivec2(0, 0), 0).r / auto_exposure_grey; + #endif -#ifdef USE_GLOW_SOFTLIGHT + color *= exposure; - { + // Early Tonemap & SRGB Conversion - glow = (glow * 0.5) + 0.5; - color.r = (glow.r <= 0.5) ? (color.r - (1.0 - 2.0 * glow.r) * color.r * (1.0 - color.r)) : (((glow.r > 0.5) && (color.r <= 0.25)) ? (color.r + (2.0 * glow.r - 1.0) * (4.0 * color.r * (4.0 * color.r + 1.0) * (color.r - 1.0) + 7.0 * color.r)) : (color.r + (2.0 * glow.r - 1.0) * (sqrt(color.r) - color.r))); - color.g = (glow.g <= 0.5) ? (color.g - (1.0 - 2.0 * glow.g) * color.g * (1.0 - color.g)) : (((glow.g > 0.5) && (color.g <= 0.25)) ? (color.g + (2.0 * glow.g - 1.0) * (4.0 * color.g * (4.0 * color.g + 1.0) * (color.g - 1.0) + 7.0 * color.g)) : (color.g + (2.0 * glow.g - 1.0) * (sqrt(color.g) - color.g))); - color.b = (glow.b <= 0.5) ? (color.b - (1.0 - 2.0 * glow.b) * color.b * (1.0 - color.b)) : (((glow.b > 0.5) && (color.b <= 0.25)) ? (color.b + (2.0 * glow.b - 1.0) * (4.0 * color.b * (4.0 * color.b + 1.0) * (color.b - 1.0) + 7.0 * color.b)) : (color.b + (2.0 * glow.b - 1.0) * (sqrt(color.b) - color.b))); - } + color = apply_tonemapping(color, white); -#endif + #ifdef KEEP_3D_LINEAR + // leave color as is (-> don't convert to SRGB) + #else + color = linear_to_srgb(color); // regular linear -> SRGB conversion + #endif -#if defined(USING_GLOW) && !defined(USE_GLOW_SCREEN) && !defined(USE_GLOW_SOFTLIGHT) && !defined(USE_GLOW_REPLACE) - //additive - color.rgb+=glow; -#endif + // Glow -#ifdef USE_BCS + #ifdef USING_GLOW + vec3 glow = gather_glow(source_glow, uv_interp) * glow_intensity; - color.rgb = mix(vec3(0.0),color.rgb,bcs.x); - color.rgb = mix(vec3(0.5),color.rgb,bcs.y); - color.rgb = mix(vec3(dot(vec3(1.0),color.rgb)*0.33333),color.rgb,bcs.z); + // high dynamic range -> SRGB + glow = apply_tonemapping(glow, white); + glow = linear_to_srgb(glow); -#endif + color = apply_glow(color, glow); + #endif -#ifdef USE_COLOR_CORRECTION + // Additional effects - color.r = texture(color_correction,vec2(color.r,0.0)).r; - color.g = texture(color_correction,vec2(color.g,0.0)).g; - color.b = texture(color_correction,vec2(color.b,0.0)).b; -#endif + #ifdef USE_BCS + color = apply_bcs(color, bcs); + #endif + #ifdef USE_COLOR_CORRECTION + color = apply_color_correction(color, color_correction); + #endif - frag_color=vec4(color.rgb,1.0); + frag_color = vec4(color, 1.0f); } diff --git a/drivers/pulseaudio/audio_driver_pulseaudio.cpp b/drivers/pulseaudio/audio_driver_pulseaudio.cpp index 0f47949b4b..d1104eec18 100644 --- a/drivers/pulseaudio/audio_driver_pulseaudio.cpp +++ b/drivers/pulseaudio/audio_driver_pulseaudio.cpp @@ -64,18 +64,32 @@ void AudioDriverPulseAudio::pa_sink_info_cb(pa_context *c, const pa_sink_info *l ad->pa_status++; } +void AudioDriverPulseAudio::pa_source_info_cb(pa_context *c, const pa_source_info *l, int eol, void *userdata) { + AudioDriverPulseAudio *ad = (AudioDriverPulseAudio *)userdata; + + // If eol is set to a positive number, you're at the end of the list + if (eol > 0) { + return; + } + + ad->pa_rec_map = l->channel_map; + ad->pa_status++; +} + void AudioDriverPulseAudio::pa_server_info_cb(pa_context *c, const pa_server_info *i, void *userdata) { AudioDriverPulseAudio *ad = (AudioDriverPulseAudio *)userdata; + ad->capture_default_device = i->default_source_name; ad->default_device = i->default_sink_name; ad->pa_status++; } -void AudioDriverPulseAudio::detect_channels() { +void AudioDriverPulseAudio::detect_channels(bool capture) { - pa_channel_map_init_stereo(&pa_map); + pa_channel_map_init_stereo(capture ? &pa_rec_map : &pa_map); - if (device_name == "Default") { + String device = capture ? capture_device_name : device_name; + if (device == "Default") { // Get the default output device name pa_status = 0; pa_operation *pa_op = pa_context_get_server_info(pa_ctx, &AudioDriverPulseAudio::pa_server_info_cb, (void *)this); @@ -93,16 +107,22 @@ void AudioDriverPulseAudio::detect_channels() { } } - char device[1024]; - if (device_name == "Default") { - strcpy(device, default_device.utf8().get_data()); + char dev[1024]; + if (device == "Default") { + strcpy(dev, capture ? capture_default_device.utf8().get_data() : default_device.utf8().get_data()); } else { - strcpy(device, device_name.utf8().get_data()); + strcpy(dev, device.utf8().get_data()); } // Now using the device name get the amount of channels pa_status = 0; - pa_operation *pa_op = pa_context_get_sink_info_by_name(pa_ctx, device, &AudioDriverPulseAudio::pa_sink_info_cb, (void *)this); + pa_operation *pa_op; + if (capture) { + pa_op = pa_context_get_source_info_by_name(pa_ctx, dev, &AudioDriverPulseAudio::pa_source_info_cb, (void *)this); + } else { + pa_op = pa_context_get_sink_info_by_name(pa_ctx, dev, &AudioDriverPulseAudio::pa_sink_info_cb, (void *)this); + } + if (pa_op) { while (pa_status == 0) { int ret = pa_mainloop_iterate(pa_ml, 1, NULL); @@ -113,7 +133,11 @@ void AudioDriverPulseAudio::detect_channels() { pa_operation_unref(pa_op); } else { - ERR_PRINT("pa_context_get_sink_info_by_name error"); + if (capture) { + ERR_PRINT("pa_context_get_source_info_by_name error"); + } else { + ERR_PRINT("pa_context_get_sink_info_by_name error"); + } } } @@ -155,7 +179,7 @@ Error AudioDriverPulseAudio::init_device() { break; } - int latency = GLOBAL_DEF("audio/output_latency", DEFAULT_OUTPUT_LATENCY); + int latency = GLOBAL_DEF_RST("audio/output_latency", DEFAULT_OUTPUT_LATENCY); buffer_frames = closest_power_of_2(latency * mix_rate / 1000); pa_buffer_size = buffer_frames * pa_map.channels; @@ -195,6 +219,10 @@ Error AudioDriverPulseAudio::init_device() { samples_in.resize(buffer_frames * channels); samples_out.resize(pa_buffer_size); + // Reset audio input to keep synchronisation. + input_position = 0; + input_size = 0; + return OK; } @@ -204,7 +232,7 @@ Error AudioDriverPulseAudio::init() { thread_exited = false; exit_thread = false; - mix_rate = GLOBAL_DEF("audio/mix_rate", DEFAULT_MIX_RATE); + mix_rate = GLOBAL_DEF_RST("audio/mix_rate", DEFAULT_MIX_RATE); pa_ml = pa_mainloop_new(); ERR_FAIL_COND_V(pa_ml == NULL, ERR_CANT_OPEN); @@ -287,75 +315,71 @@ float AudioDriverPulseAudio::get_latency() { void AudioDriverPulseAudio::thread_func(void *p_udata) { AudioDriverPulseAudio *ad = (AudioDriverPulseAudio *)p_udata; + unsigned int write_ofs = 0; + size_t avail_bytes = 0; while (!ad->exit_thread) { - if (!ad->active) { - for (unsigned int i = 0; i < ad->pa_buffer_size; i++) { - ad->samples_out[i] = 0; - } - - } else { - ad->lock(); - ad->audio_server_process(ad->buffer_frames, ad->samples_in.ptrw()); + size_t read_bytes = 0; + size_t written_bytes = 0; - ad->unlock(); + if (avail_bytes == 0) { + ad->lock(); + ad->start_counting_ticks(); - if (ad->channels == ad->pa_map.channels) { + if (!ad->active) { for (unsigned int i = 0; i < ad->pa_buffer_size; i++) { - ad->samples_out[i] = ad->samples_in[i] >> 16; + ad->samples_out.write[i] = 0; } } else { - // Uneven amount of channels - unsigned int in_idx = 0; - unsigned int out_idx = 0; + ad->audio_server_process(ad->buffer_frames, ad->samples_in.ptrw()); - for (unsigned int i = 0; i < ad->buffer_frames; i++) { - for (unsigned int j = 0; j < ad->pa_map.channels - 1; j++) { - ad->samples_out[out_idx++] = ad->samples_in[in_idx++] >> 16; + if (ad->channels == ad->pa_map.channels) { + for (unsigned int i = 0; i < ad->pa_buffer_size; i++) { + ad->samples_out.write[i] = ad->samples_in[i] >> 16; + } + } else { + // Uneven amount of channels + unsigned int in_idx = 0; + unsigned int out_idx = 0; + + for (unsigned int i = 0; i < ad->buffer_frames; i++) { + for (unsigned int j = 0; j < ad->pa_map.channels - 1; j++) { + ad->samples_out.write[out_idx++] = ad->samples_in[in_idx++] >> 16; + } + uint32_t l = ad->samples_in[in_idx++]; + uint32_t r = ad->samples_in[in_idx++]; + ad->samples_out.write[out_idx++] = ((l >> 1) + (r >> 1)) >> 16; } - uint32_t l = ad->samples_in[in_idx++]; - uint32_t r = ad->samples_in[in_idx++]; - ad->samples_out[out_idx++] = (l >> 1 + r >> 1) >> 16; } } - } - int error_code; - int byte_size = ad->pa_buffer_size * sizeof(int16_t); + avail_bytes = ad->pa_buffer_size * sizeof(int16_t); + write_ofs = 0; + ad->stop_counting_ticks(); + ad->unlock(); + } ad->lock(); + ad->start_counting_ticks(); int ret; do { ret = pa_mainloop_iterate(ad->pa_ml, 0, NULL); } while (ret > 0); - if (pa_stream_get_state(ad->pa_str) == PA_STREAM_READY) { - const void *ptr = ad->samples_out.ptr(); - while (byte_size > 0) { - size_t bytes = pa_stream_writable_size(ad->pa_str); - if (bytes > 0) { - if (bytes > byte_size) { - bytes = byte_size; - } - - ret = pa_stream_write(ad->pa_str, ptr, bytes, NULL, 0LL, PA_SEEK_RELATIVE); - if (ret >= 0) { - byte_size -= bytes; - ptr = (const char *)ptr + bytes; - } + if (avail_bytes > 0 && pa_stream_get_state(ad->pa_str) == PA_STREAM_READY) { + size_t bytes = pa_stream_writable_size(ad->pa_str); + if (bytes > 0) { + size_t bytes_to_write = MIN(bytes, avail_bytes); + const void *ptr = ad->samples_out.ptr(); + ret = pa_stream_write(ad->pa_str, (char *)ptr + write_ofs, bytes_to_write, NULL, 0LL, PA_SEEK_RELATIVE); + if (ret != 0) { + ERR_PRINT("pa_stream_write error"); } else { - ret = pa_mainloop_iterate(ad->pa_ml, 0, NULL); - if (ret == 0) { - // If pa_mainloop_iterate returns 0 sleep for 1 msec to wait - // for the stream to be able to process more bytes - ad->unlock(); - - OS::get_singleton()->delay_usec(1000); - - ad->lock(); - } + avail_bytes -= bytes_to_write; + write_ofs += bytes_to_write; + written_bytes += bytes_to_write; } } } @@ -380,7 +404,64 @@ void AudioDriverPulseAudio::thread_func(void *p_udata) { } } + if (ad->pa_rec_str && pa_stream_get_state(ad->pa_rec_str) == PA_STREAM_READY) { + size_t bytes = pa_stream_readable_size(ad->pa_rec_str); + if (bytes > 0) { + const void *ptr = NULL; + size_t maxbytes = ad->input_buffer.size() * sizeof(int16_t); + + bytes = MIN(bytes, maxbytes); + ret = pa_stream_peek(ad->pa_rec_str, &ptr, &bytes); + if (ret != 0) { + ERR_PRINT("pa_stream_peek error"); + } else { + int16_t *srcptr = (int16_t *)ptr; + for (size_t i = bytes >> 1; i > 0; i--) { + int32_t sample = int32_t(*srcptr++) << 16; + ad->input_buffer_write(sample); + + if (ad->pa_rec_map.channels == 1) { + // In case input device is single channel convert it to Stereo + ad->input_buffer_write(sample); + } + } + + read_bytes += bytes; + ret = pa_stream_drop(ad->pa_rec_str); + if (ret != 0) { + ERR_PRINT("pa_stream_drop error"); + } + } + } + + // User selected a new device, finish the current one so we'll init the new device + if (ad->capture_device_name != ad->capture_new_device) { + ad->capture_device_name = ad->capture_new_device; + ad->capture_finish_device(); + + Error err = ad->capture_init_device(); + if (err != OK) { + ERR_PRINT("PulseAudio: capture_init_device error"); + ad->capture_device_name = "Default"; + ad->capture_new_device = "Default"; + + err = ad->capture_init_device(); + if (err != OK) { + ad->active = false; + ad->exit_thread = true; + break; + } + } + } + } + + ad->stop_counting_ticks(); ad->unlock(); + + // Let the thread rest a while if we haven't read or write anything + if (written_bytes == 0 && read_bytes == 0) { + OS::get_singleton()->delay_usec(1000); + } } ad->thread_exited = true; @@ -403,7 +484,6 @@ AudioDriver::SpeakerMode AudioDriverPulseAudio::get_speaker_mode() const { void AudioDriverPulseAudio::pa_sinklist_cb(pa_context *c, const pa_sink_info *l, int eol, void *userdata) { AudioDriverPulseAudio *ad = (AudioDriverPulseAudio *)userdata; - int ctr = 0; // If eol is set to a positive number, you're at the end of the list if (eol > 0) { @@ -453,7 +533,9 @@ String AudioDriverPulseAudio::get_device() { void AudioDriverPulseAudio::set_device(String device) { + lock(); new_device = device; + unlock(); } void AudioDriverPulseAudio::lock() { @@ -509,11 +591,165 @@ void AudioDriverPulseAudio::finish() { thread = NULL; } +Error AudioDriverPulseAudio::capture_init_device() { + + // If there is a specified device check that it is really present + if (capture_device_name != "Default") { + Array list = capture_get_device_list(); + if (list.find(capture_device_name) == -1) { + capture_device_name = "Default"; + capture_new_device = "Default"; + } + } + + detect_channels(true); + switch (pa_rec_map.channels) { + case 1: // Mono + case 2: // Stereo + break; + + default: + WARN_PRINTS("PulseAudio: Unsupported number of input channels: " + itos(pa_rec_map.channels)); + pa_channel_map_init_stereo(&pa_rec_map); + break; + } + + if (OS::get_singleton()->is_stdout_verbose()) { + print_line("PulseAudio: detected " + itos(pa_rec_map.channels) + " input channels"); + } + + pa_sample_spec spec; + + spec.format = PA_SAMPLE_S16LE; + spec.channels = pa_rec_map.channels; + spec.rate = mix_rate; + + int latency = 30; + input_buffer_frames = closest_power_of_2(latency * mix_rate / 1000); + int buffer_size = input_buffer_frames * spec.channels; + + pa_buffer_attr attr; + attr.fragsize = buffer_size * sizeof(int16_t); + + pa_rec_str = pa_stream_new(pa_ctx, "Record", &spec, &pa_rec_map); + if (pa_rec_str == NULL) { + ERR_PRINTS("PulseAudio: pa_stream_new error: " + String(pa_strerror(pa_context_errno(pa_ctx)))); + ERR_FAIL_V(ERR_CANT_OPEN); + } + + const char *dev = capture_device_name == "Default" ? NULL : capture_device_name.utf8().get_data(); + pa_stream_flags flags = pa_stream_flags(PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_ADJUST_LATENCY | PA_STREAM_AUTO_TIMING_UPDATE); + int error_code = pa_stream_connect_record(pa_rec_str, dev, &attr, flags); + if (error_code < 0) { + ERR_PRINTS("PulseAudio: pa_stream_connect_record error: " + String(pa_strerror(error_code))); + ERR_FAIL_V(ERR_CANT_OPEN); + } + + input_buffer.resize(input_buffer_frames * 8); + input_position = 0; + input_size = 0; + + return OK; +} + +void AudioDriverPulseAudio::capture_finish_device() { + + if (pa_rec_str) { + int ret = pa_stream_disconnect(pa_rec_str); + if (ret != 0) { + ERR_PRINTS("PulseAudio: pa_stream_disconnect error: " + String(pa_strerror(ret))); + } + pa_stream_unref(pa_rec_str); + pa_rec_str = NULL; + } +} + +Error AudioDriverPulseAudio::capture_start() { + + lock(); + Error err = capture_init_device(); + unlock(); + + return err; +} + +Error AudioDriverPulseAudio::capture_stop() { + lock(); + capture_finish_device(); + unlock(); + + return OK; +} + +void AudioDriverPulseAudio::capture_set_device(const String &p_name) { + + lock(); + capture_new_device = p_name; + unlock(); +} + +void AudioDriverPulseAudio::pa_sourcelist_cb(pa_context *c, const pa_source_info *l, int eol, void *userdata) { + AudioDriverPulseAudio *ad = (AudioDriverPulseAudio *)userdata; + + // If eol is set to a positive number, you're at the end of the list + if (eol > 0) { + return; + } + + if (l->monitor_of_sink == PA_INVALID_INDEX) { + ad->pa_rec_devices.push_back(l->name); + } + + ad->pa_status++; +} + +Array AudioDriverPulseAudio::capture_get_device_list() { + + pa_rec_devices.clear(); + pa_rec_devices.push_back("Default"); + + if (pa_ctx == NULL) { + return pa_rec_devices; + } + + lock(); + + // Get the device list + pa_status = 0; + pa_operation *pa_op = pa_context_get_source_info_list(pa_ctx, pa_sourcelist_cb, (void *)this); + if (pa_op) { + while (pa_status == 0) { + int ret = pa_mainloop_iterate(pa_ml, 1, NULL); + if (ret < 0) { + ERR_PRINT("pa_mainloop_iterate error"); + } + } + + pa_operation_unref(pa_op); + } else { + ERR_PRINT("pa_context_get_server_info error"); + } + + unlock(); + + return pa_rec_devices; +} + +String AudioDriverPulseAudio::capture_get_device() { + + lock(); + String name = capture_device_name; + unlock(); + + return name; +} + AudioDriverPulseAudio::AudioDriverPulseAudio() { pa_ml = NULL; pa_ctx = NULL; pa_str = NULL; + pa_rec_str = NULL; mutex = NULL; thread = NULL; @@ -527,6 +763,7 @@ AudioDriverPulseAudio::AudioDriverPulseAudio() { mix_rate = 0; buffer_frames = 0; + input_buffer_frames = 0; pa_buffer_size = 0; channels = 0; pa_ready = 0; diff --git a/drivers/pulseaudio/audio_driver_pulseaudio.h b/drivers/pulseaudio/audio_driver_pulseaudio.h index b471f5f9d5..f8358a452b 100644 --- a/drivers/pulseaudio/audio_driver_pulseaudio.h +++ b/drivers/pulseaudio/audio_driver_pulseaudio.h @@ -47,22 +47,30 @@ class AudioDriverPulseAudio : public AudioDriver { pa_mainloop *pa_ml; pa_context *pa_ctx; pa_stream *pa_str; + pa_stream *pa_rec_str; pa_channel_map pa_map; + pa_channel_map pa_rec_map; String device_name; String new_device; String default_device; + String capture_device_name; + String capture_new_device; + String capture_default_device; + Vector<int32_t> samples_in; Vector<int16_t> samples_out; unsigned int mix_rate; unsigned int buffer_frames; + unsigned int input_buffer_frames; unsigned int pa_buffer_size; int channels; int pa_ready; int pa_status; Array pa_devices; + Array pa_rec_devices; bool active; bool thread_exited; @@ -72,13 +80,18 @@ class AudioDriverPulseAudio : public AudioDriver { static void pa_state_cb(pa_context *c, void *userdata); static void pa_sink_info_cb(pa_context *c, const pa_sink_info *l, int eol, void *userdata); + static void pa_source_info_cb(pa_context *c, const pa_source_info *l, int eol, void *userdata); static void pa_server_info_cb(pa_context *c, const pa_server_info *i, void *userdata); static void pa_sinklist_cb(pa_context *c, const pa_sink_info *l, int eol, void *userdata); + static void pa_sourcelist_cb(pa_context *c, const pa_source_info *l, int eol, void *userdata); Error init_device(); void finish_device(); - void detect_channels(); + Error capture_init_device(); + void capture_finish_device(); + + void detect_channels(bool capture = false); static void thread_func(void *p_udata); @@ -91,15 +104,24 @@ public: virtual void start(); virtual int get_mix_rate() const; virtual SpeakerMode get_speaker_mode() const; + virtual Array get_device_list(); virtual String get_device(); virtual void set_device(String device); + + virtual Array capture_get_device_list(); + virtual void capture_set_device(const String &p_name); + virtual String capture_get_device(); + virtual void lock(); virtual void unlock(); virtual void finish(); virtual float get_latency(); + virtual Error capture_start(); + virtual Error capture_stop(); + AudioDriverPulseAudio(); ~AudioDriverPulseAudio(); }; diff --git a/drivers/rtaudio/audio_driver_rtaudio.cpp b/drivers/rtaudio/audio_driver_rtaudio.cpp index 457486797f..365788e192 100644 --- a/drivers/rtaudio/audio_driver_rtaudio.cpp +++ b/drivers/rtaudio/audio_driver_rtaudio.cpp @@ -88,7 +88,7 @@ Error AudioDriverRtAudio::init() { // FIXME: Adapt to the OutputFormat -> SpeakerMode change /* - String channels = GLOBAL_DEF("audio/output","stereo"); + String channels = GLOBAL_DEF_RST("audio/output","stereo"); if (channels=="5.1") output_format=OUTPUT_5_1; @@ -108,7 +108,7 @@ Error AudioDriverRtAudio::init() { options.numberOfBuffers = 4; parameters.firstChannel = 0; - mix_rate = GLOBAL_DEF("audio/mix_rate", DEFAULT_MIX_RATE); + mix_rate = GLOBAL_DEF_RST("audio/mix_rate", DEFAULT_MIX_RATE); int latency = GLOBAL_DEF("audio/output_latency", DEFAULT_OUTPUT_LATENCY); unsigned int buffer_frames = closest_power_of_2(latency * mix_rate / 1000); diff --git a/drivers/unix/os_unix.cpp b/drivers/unix/os_unix.cpp index 05dfd69f58..8aab4cb521 100644 --- a/drivers/unix/os_unix.cpp +++ b/drivers/unix/os_unix.cpp @@ -329,7 +329,7 @@ Error OS_Unix::execute(const String &p_path, const List<String> &p_arguments, bo return OK; } -Error OS_Unix::kill(const ProcessID &p_pid) { +Error OS_Unix::kill(const ProcessID &p_pid, const int p_max_wait_msec) { int ret = ::kill(p_pid, SIGKILL); if (!ret) { diff --git a/drivers/unix/os_unix.h b/drivers/unix/os_unix.h index 95b74d23ff..c5240231fa 100644 --- a/drivers/unix/os_unix.h +++ b/drivers/unix/os_unix.h @@ -91,7 +91,7 @@ public: virtual uint64_t get_ticks_usec() const; virtual Error execute(const String &p_path, const List<String> &p_arguments, bool p_blocking, ProcessID *r_child_id = NULL, String *r_pipe = NULL, int *r_exitcode = NULL, bool read_stderr = false); - virtual Error kill(const ProcessID &p_pid); + virtual Error kill(const ProcessID &p_pid, const int p_max_wait_msec = -1); virtual int get_process_id() const; virtual bool has_environment(const String &p_var) const; diff --git a/drivers/wasapi/audio_driver_wasapi.cpp b/drivers/wasapi/audio_driver_wasapi.cpp index e1680601ad..2dcb4ff3d8 100644 --- a/drivers/wasapi/audio_driver_wasapi.cpp +++ b/drivers/wasapi/audio_driver_wasapi.cpp @@ -52,8 +52,22 @@ const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator); const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator); const IID IID_IAudioClient = __uuidof(IAudioClient); const IID IID_IAudioRenderClient = __uuidof(IAudioRenderClient); +const IID IID_IAudioCaptureClient = __uuidof(IAudioCaptureClient); -static bool default_device_changed = false; +#define SAFE_RELEASE(memory) \ + if ((memory) != NULL) { \ + (memory)->Release(); \ + (memory) = NULL; \ + } + +#define REFTIMES_PER_SEC 10000000 +#define REFTIMES_PER_MILLISEC 10000 + +#define CAPTURE_BUFFER_CHANNELS 2 + +static StringName capture_device_id; +static bool default_render_device_changed = false; +static bool default_capture_device_changed = false; class CMMNotificationClient : public IMMNotificationClient { LONG _cRef; @@ -109,8 +123,13 @@ public: } HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR pwstrDeviceId) { - if (flow == eRender && role == eConsole) { - default_device_changed = true; + if (role == eConsole) { + if (flow == eRender) { + default_render_device_changed = true; + } else if (flow == eCapture) { + default_capture_device_changed = true; + capture_device_id = String(pwstrDeviceId); + } } return S_OK; @@ -123,7 +142,7 @@ public: static CMMNotificationClient notif_client; -Error AudioDriverWASAPI::init_device(bool reinit) { +Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_capture, bool reinit) { WAVEFORMATEX *pwfex; IMMDeviceEnumerator *enumerator = NULL; @@ -134,12 +153,12 @@ Error AudioDriverWASAPI::init_device(bool reinit) { HRESULT hr = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void **)&enumerator); ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); - if (device_name == "Default") { - hr = enumerator->GetDefaultAudioEndpoint(eRender, eConsole, &device); + if (p_device->device_name == "Default") { + hr = enumerator->GetDefaultAudioEndpoint(p_capture ? eCapture : eRender, eConsole, &device); } else { IMMDeviceCollection *devices = NULL; - hr = enumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &devices); + hr = enumerator->EnumAudioEndpoints(p_capture ? eCapture : eRender, DEVICE_STATE_ACTIVE, &devices); ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); LPWSTR strId = NULL; @@ -165,7 +184,7 @@ Error AudioDriverWASAPI::init_device(bool reinit) { hr = props->GetValue(PKEY_Device_FriendlyName, &propvar); ERR_BREAK(hr != S_OK); - if (device_name == String(propvar.pwszVal)) { + if (p_device->device_name == String(propvar.pwszVal)) { hr = device->GetId(&strId); ERR_BREAK(hr != S_OK); @@ -186,9 +205,10 @@ Error AudioDriverWASAPI::init_device(bool reinit) { } if (device == NULL) { - hr = enumerator->GetDefaultAudioEndpoint(eRender, eConsole, &device); + hr = enumerator->GetDefaultAudioEndpoint(p_capture ? eCapture : eRender, eConsole, &device); } } + if (reinit) { // In case we're trying to re-initialize the device prevent throwing this error on the console, // otherwise if there is currently no device available this will spam the console. @@ -200,11 +220,15 @@ Error AudioDriverWASAPI::init_device(bool reinit) { } hr = enumerator->RegisterEndpointNotificationCallback(¬if_client); + SAFE_RELEASE(enumerator) + if (hr != S_OK) { ERR_PRINT("WASAPI: RegisterEndpointNotificationCallback error"); } - hr = device->Activate(IID_IAudioClient, CLSCTX_ALL, NULL, (void **)&audio_client); + hr = device->Activate(IID_IAudioClient, CLSCTX_ALL, NULL, (void **)&p_device->audio_client); + SAFE_RELEASE(device) + if (reinit) { if (hr != S_OK) { return ERR_CANT_OPEN; @@ -213,75 +237,89 @@ Error AudioDriverWASAPI::init_device(bool reinit) { ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); } - hr = audio_client->GetMixFormat(&pwfex); + hr = p_device->audio_client->GetMixFormat(&pwfex); ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); // Since we're using WASAPI Shared Mode we can't control any of these, we just tag along - wasapi_channels = pwfex->nChannels; - format_tag = pwfex->wFormatTag; - bits_per_sample = pwfex->wBitsPerSample; - - switch (wasapi_channels) { - case 2: // Stereo - case 4: // Surround 3.1 - case 6: // Surround 5.1 - case 8: // Surround 7.1 - channels = wasapi_channels; - break; + p_device->channels = pwfex->nChannels; + p_device->format_tag = pwfex->wFormatTag; + p_device->bits_per_sample = pwfex->wBitsPerSample; + p_device->frame_size = (p_device->bits_per_sample / 8) * p_device->channels; - default: - WARN_PRINTS("WASAPI: Unsupported number of channels: " + itos(wasapi_channels)); - channels = 2; - break; - } - - if (format_tag == WAVE_FORMAT_EXTENSIBLE) { + if (p_device->format_tag == WAVE_FORMAT_EXTENSIBLE) { WAVEFORMATEXTENSIBLE *wfex = (WAVEFORMATEXTENSIBLE *)pwfex; if (wfex->SubFormat == KSDATAFORMAT_SUBTYPE_PCM) { - format_tag = WAVE_FORMAT_PCM; + p_device->format_tag = WAVE_FORMAT_PCM; } else if (wfex->SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT) { - format_tag = WAVE_FORMAT_IEEE_FLOAT; + p_device->format_tag = WAVE_FORMAT_IEEE_FLOAT; } else { ERR_PRINT("WASAPI: Format not supported"); ERR_FAIL_V(ERR_CANT_OPEN); } } else { - if (format_tag != WAVE_FORMAT_PCM && format_tag != WAVE_FORMAT_IEEE_FLOAT) { + if (p_device->format_tag != WAVE_FORMAT_PCM && p_device->format_tag != WAVE_FORMAT_IEEE_FLOAT) { ERR_PRINT("WASAPI: Format not supported"); ERR_FAIL_V(ERR_CANT_OPEN); } } - DWORD streamflags = AUDCLNT_STREAMFLAGS_EVENTCALLBACK; + DWORD streamflags = 0; if (mix_rate != pwfex->nSamplesPerSec) { streamflags |= AUDCLNT_STREAMFLAGS_RATEADJUST; pwfex->nSamplesPerSec = mix_rate; pwfex->nAvgBytesPerSec = pwfex->nSamplesPerSec * pwfex->nChannels * (pwfex->wBitsPerSample / 8); } - hr = audio_client->Initialize(AUDCLNT_SHAREMODE_SHARED, streamflags, 0, 0, pwfex, NULL); + hr = p_device->audio_client->Initialize(AUDCLNT_SHAREMODE_SHARED, streamflags, p_capture ? REFTIMES_PER_SEC : 0, 0, pwfex, NULL); ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); - event = CreateEvent(NULL, FALSE, FALSE, NULL); - ERR_FAIL_COND_V(event == NULL, ERR_CANT_OPEN); - - hr = audio_client->SetEventHandle(event); + if (p_capture) { + hr = p_device->audio_client->GetService(IID_IAudioCaptureClient, (void **)&p_device->capture_client); + } else { + hr = p_device->audio_client->GetService(IID_IAudioRenderClient, (void **)&p_device->render_client); + } ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); - hr = audio_client->GetService(IID_IAudioRenderClient, (void **)&render_client); - ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); + // Free memory + CoTaskMemFree(pwfex); + SAFE_RELEASE(device) + + return OK; +} + +Error AudioDriverWASAPI::init_render_device(bool reinit) { + + Error err = audio_device_init(&audio_output, false, reinit); + if (err != OK) + return err; + + switch (audio_output.channels) { + case 2: // Stereo + case 4: // Surround 3.1 + case 6: // Surround 5.1 + case 8: // Surround 7.1 + channels = audio_output.channels; + break; + + default: + WARN_PRINTS("WASAPI: Unsupported number of channels: " + itos(audio_output.channels)); + channels = 2; + break; + } UINT32 max_frames; - hr = audio_client->GetBufferSize(&max_frames); + HRESULT hr = audio_output.audio_client->GetBufferSize(&max_frames); ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); // Due to WASAPI Shared Mode we have no control of the buffer size buffer_frames = max_frames; // Sample rate is independent of channels (ref: https://stackoverflow.com/questions/11048825/audio-sample-frequency-rely-on-channels) - buffer_size = buffer_frames * channels; - samples_in.resize(buffer_size); + samples_in.resize(buffer_frames * channels); + + input_position = 0; + input_size = 0; if (OS::get_singleton()->is_stdout_verbose()) { print_line("WASAPI: detected " + itos(channels) + " channels"); @@ -291,41 +329,61 @@ Error AudioDriverWASAPI::init_device(bool reinit) { return OK; } -Error AudioDriverWASAPI::finish_device() { +Error AudioDriverWASAPI::init_capture_device(bool reinit) { - if (audio_client) { - if (active) { - audio_client->Stop(); - active = false; - } + Error err = audio_device_init(&audio_input, true, reinit); + if (err != OK) + return err; - audio_client->Release(); - audio_client = NULL; - } + // Get the max frames + UINT32 max_frames; + HRESULT hr = audio_input.audio_client->GetBufferSize(&max_frames); + ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); - if (render_client) { - render_client->Release(); - render_client = NULL; - } + // Set the buffer size + input_buffer.resize(max_frames * CAPTURE_BUFFER_CHANNELS); + input_position = 0; + input_size = 0; + + return OK; +} + +Error AudioDriverWASAPI::audio_device_finish(AudioDeviceWASAPI *p_device) { - if (audio_client) { - audio_client->Release(); - audio_client = NULL; + if (p_device->active) { + if (p_device->audio_client) { + p_device->audio_client->Stop(); + } + + p_device->active = false; } + SAFE_RELEASE(p_device->audio_client) + SAFE_RELEASE(p_device->render_client) + SAFE_RELEASE(p_device->capture_client) + return OK; } +Error AudioDriverWASAPI::finish_render_device() { + + return audio_device_finish(&audio_output); +} + +Error AudioDriverWASAPI::finish_capture_device() { + + return audio_device_finish(&audio_input); +} + Error AudioDriverWASAPI::init() { - mix_rate = GLOBAL_DEF("audio/mix_rate", DEFAULT_MIX_RATE); + mix_rate = GLOBAL_DEF_RST("audio/mix_rate", DEFAULT_MIX_RATE); - Error err = init_device(); + Error err = init_render_device(); if (err != OK) { - ERR_PRINT("WASAPI: init_device error"); + ERR_PRINT("WASAPI: init_render_device error"); } - active = false; exit_thread = false; thread_exited = false; @@ -335,22 +393,6 @@ Error AudioDriverWASAPI::init() { return OK; } -Error AudioDriverWASAPI::reopen() { - Error err = finish_device(); - if (err != OK) { - ERR_PRINT("WASAPI: finish_device error"); - } else { - err = init_device(); - if (err != OK) { - ERR_PRINT("WASAPI: init_device error"); - } else { - start(); - } - } - - return err; -} - int AudioDriverWASAPI::get_mix_rate() const { return mix_rate; @@ -361,7 +403,7 @@ AudioDriver::SpeakerMode AudioDriverWASAPI::get_speaker_mode() const { return get_speaker_mode_by_total_channels(channels); } -Array AudioDriverWASAPI::get_device_list() { +Array AudioDriverWASAPI::audio_device_get_list(bool p_capture) { Array list; IMMDeviceCollection *devices = NULL; @@ -374,7 +416,7 @@ Array AudioDriverWASAPI::get_device_list() { HRESULT hr = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void **)&enumerator); ERR_FAIL_COND_V(hr != S_OK, Array()); - hr = enumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &devices); + hr = enumerator->EnumAudioEndpoints(p_capture ? eCapture : eRender, DEVICE_STATE_ACTIVE, &devices); ERR_FAIL_COND_V(hr != S_OK, Array()); UINT count = 0; @@ -409,19 +451,63 @@ Array AudioDriverWASAPI::get_device_list() { return list; } +Array AudioDriverWASAPI::get_device_list() { + + return audio_device_get_list(false); +} + String AudioDriverWASAPI::get_device() { - return device_name; + lock(); + String name = audio_output.device_name; + unlock(); + + return name; } void AudioDriverWASAPI::set_device(String device) { - new_device = device; + lock(); + audio_output.new_device = device; + unlock(); +} + +int32_t AudioDriverWASAPI::read_sample(WORD format_tag, int bits_per_sample, BYTE *buffer, int i) { + if (format_tag == WAVE_FORMAT_PCM) { + int32_t sample = 0; + switch (bits_per_sample) { + case 8: + sample = int32_t(((int8_t *)buffer)[i]) << 24; + break; + + case 16: + sample = int32_t(((int16_t *)buffer)[i]) << 16; + break; + + case 24: + sample |= int32_t(((int8_t *)buffer)[i * 3 + 2]) << 24; + sample |= int32_t(((int8_t *)buffer)[i * 3 + 1]) << 16; + sample |= int32_t(((int8_t *)buffer)[i * 3 + 0]) << 8; + break; + + case 32: + sample = ((int32_t *)buffer)[i]; + break; + } + + return sample; + } else if (format_tag == WAVE_FORMAT_IEEE_FLOAT) { + return int32_t(((float *)buffer)[i] * 32768.0) << 16; + } else { + ERR_PRINT("WASAPI: Unknown format tag"); + } + + return 0; } -void AudioDriverWASAPI::write_sample(AudioDriverWASAPI *ad, BYTE *buffer, int i, int32_t sample) { - if (ad->format_tag == WAVE_FORMAT_PCM) { - switch (ad->bits_per_sample) { +void AudioDriverWASAPI::write_sample(WORD format_tag, int bits_per_sample, BYTE *buffer, int i, int32_t sample) { + if (format_tag == WAVE_FORMAT_PCM) { + switch (bits_per_sample) { case 8: ((int8_t *)buffer)[i] = sample >> 24; break; @@ -440,125 +526,221 @@ void AudioDriverWASAPI::write_sample(AudioDriverWASAPI *ad, BYTE *buffer, int i, ((int32_t *)buffer)[i] = sample; break; } - } else if (ad->format_tag == WAVE_FORMAT_IEEE_FLOAT) { + } else if (format_tag == WAVE_FORMAT_IEEE_FLOAT) { ((float *)buffer)[i] = (sample >> 16) / 32768.f; } else { ERR_PRINT("WASAPI: Unknown format tag"); - ad->exit_thread = true; } } void AudioDriverWASAPI::thread_func(void *p_udata) { AudioDriverWASAPI *ad = (AudioDriverWASAPI *)p_udata; + uint32_t avail_frames = 0; + uint32_t write_ofs = 0; while (!ad->exit_thread) { - if (ad->active) { + + uint32_t read_frames = 0; + uint32_t written_frames = 0; + + if (avail_frames == 0) { ad->lock(); + ad->start_counting_ticks(); + + if (ad->audio_output.active) { + ad->audio_server_process(ad->buffer_frames, ad->samples_in.ptrw()); + } else { + for (unsigned int i = 0; i < ad->samples_in.size(); i++) { + ad->samples_in.write[i] = 0; + } + } - ad->audio_server_process(ad->buffer_frames, ad->samples_in.ptrw()); + avail_frames = ad->buffer_frames; + write_ofs = 0; + ad->stop_counting_ticks(); ad->unlock(); - } else { - for (unsigned int i = 0; i < ad->buffer_size; i++) { - ad->samples_in[i] = 0; - } } - unsigned int left_frames = ad->buffer_frames; - unsigned int buffer_idx = 0; - while (left_frames > 0 && ad->audio_client) { - WaitForSingleObject(ad->event, 1000); + ad->lock(); + ad->start_counting_ticks(); + + if (avail_frames > 0 && ad->audio_output.audio_client) { UINT32 cur_frames; - HRESULT hr = ad->audio_client->GetCurrentPadding(&cur_frames); + bool invalidated = false; + HRESULT hr = ad->audio_output.audio_client->GetCurrentPadding(&cur_frames); if (hr == S_OK) { - // Check how much frames are available on the WASAPI buffer - UINT32 avail_frames = ad->buffer_frames - cur_frames; - UINT32 write_frames = avail_frames > left_frames ? left_frames : avail_frames; - - BYTE *buffer = NULL; - hr = ad->render_client->GetBuffer(write_frames, &buffer); - if (hr == S_OK) { - // We're using WASAPI Shared Mode so we must convert the buffer - if (ad->channels == ad->wasapi_channels) { - for (unsigned int i = 0; i < write_frames * ad->channels; i++) { - ad->write_sample(ad, buffer, i, ad->samples_in[buffer_idx++]); - } - } else { - for (unsigned int i = 0; i < write_frames; i++) { - for (unsigned int j = 0; j < MIN(ad->channels, ad->wasapi_channels); j++) { - ad->write_sample(ad, buffer, i * ad->wasapi_channels + j, ad->samples_in[buffer_idx++]); + // Check how much frames are available on the WASAPI buffer + UINT32 write_frames = MIN(ad->buffer_frames - cur_frames, avail_frames); + if (write_frames > 0) { + BYTE *buffer = NULL; + hr = ad->audio_output.render_client->GetBuffer(write_frames, &buffer); + if (hr == S_OK) { + + // We're using WASAPI Shared Mode so we must convert the buffer + if (ad->channels == ad->audio_output.channels) { + for (unsigned int i = 0; i < write_frames * ad->channels; i++) { + ad->write_sample(ad->audio_output.format_tag, ad->audio_output.bits_per_sample, buffer, i, ad->samples_in.write[write_ofs++]); } - if (ad->wasapi_channels > ad->channels) { - for (unsigned int j = ad->channels; j < ad->wasapi_channels; j++) { - ad->write_sample(ad, buffer, i * ad->wasapi_channels + j, 0); + } else { + for (unsigned int i = 0; i < write_frames; i++) { + for (unsigned int j = 0; j < MIN(ad->channels, ad->audio_output.channels); j++) { + ad->write_sample(ad->audio_output.format_tag, ad->audio_output.bits_per_sample, buffer, i * ad->audio_output.channels + j, ad->samples_in.write[write_ofs++]); + } + if (ad->audio_output.channels > ad->channels) { + for (unsigned int j = ad->channels; j < ad->audio_output.channels; j++) { + ad->write_sample(ad->audio_output.format_tag, ad->audio_output.bits_per_sample, buffer, i * ad->audio_output.channels + j, 0); + } } } } - } - hr = ad->render_client->ReleaseBuffer(write_frames, 0); - if (hr != S_OK) { - ERR_PRINT("WASAPI: Release buffer error"); - } - - left_frames -= write_frames; - } else if (hr == AUDCLNT_E_DEVICE_INVALIDATED) { - // Device is not valid anymore, reopen it + hr = ad->audio_output.render_client->ReleaseBuffer(write_frames, 0); + if (hr != S_OK) { + ERR_PRINT("WASAPI: Release buffer error"); + } - Error err = ad->finish_device(); - if (err != OK) { - ERR_PRINT("WASAPI: finish_device error"); + avail_frames -= write_frames; + written_frames += write_frames; + } else if (hr == AUDCLNT_E_DEVICE_INVALIDATED) { + // Device is not valid anymore, reopen it + + Error err = ad->finish_render_device(); + if (err != OK) { + ERR_PRINT("WASAPI: finish_render_device error"); + } else { + // We reopened the device and samples_in may have resized, so invalidate the current avail_frames + avail_frames = 0; + } } else { - // We reopened the device and samples_in may have resized, so invalidate the current left_frames - left_frames = 0; + ERR_PRINT("WASAPI: Get buffer error"); + ad->exit_thread = true; } - } else { - ERR_PRINT("WASAPI: Get buffer error"); - ad->exit_thread = true; } } else if (hr == AUDCLNT_E_DEVICE_INVALIDATED) { - // Device is not valid anymore, reopen it + invalidated = true; + } else { + ERR_PRINT("WASAPI: GetCurrentPadding error"); + } - Error err = ad->finish_device(); + if (invalidated) { + // Device is not valid anymore + WARN_PRINT("WASAPI: Current device invalidated, closing device"); + + Error err = ad->finish_render_device(); if (err != OK) { - ERR_PRINT("WASAPI: finish_device error"); - } else { - // We reopened the device and samples_in may have resized, so invalidate the current left_frames - left_frames = 0; + ERR_PRINT("WASAPI: finish_render_device error"); } - } else { - ERR_PRINT("WASAPI: GetCurrentPadding error"); } } // If we're using the Default device and it changed finish it so we'll re-init the device - if (ad->device_name == "Default" && default_device_changed) { - Error err = ad->finish_device(); + if (ad->audio_output.device_name == "Default" && default_render_device_changed) { + Error err = ad->finish_render_device(); if (err != OK) { - ERR_PRINT("WASAPI: finish_device error"); + ERR_PRINT("WASAPI: finish_render_device error"); } - default_device_changed = false; + default_render_device_changed = false; } // User selected a new device, finish the current one so we'll init the new device - if (ad->device_name != ad->new_device) { - ad->device_name = ad->new_device; - Error err = ad->finish_device(); + if (ad->audio_output.device_name != ad->audio_output.new_device) { + ad->audio_output.device_name = ad->audio_output.new_device; + Error err = ad->finish_render_device(); if (err != OK) { - ERR_PRINT("WASAPI: finish_device error"); + ERR_PRINT("WASAPI: finish_render_device error"); } } - if (!ad->audio_client) { - Error err = ad->init_device(true); + if (!ad->audio_output.audio_client) { + Error err = ad->init_render_device(true); if (err == OK) { ad->start(); } } + + if (ad->audio_input.active) { + UINT32 packet_length = 0; + BYTE *data; + UINT32 num_frames_available; + DWORD flags; + + HRESULT hr = ad->audio_input.capture_client->GetNextPacketSize(&packet_length); + if (hr == S_OK) { + while (packet_length != 0) { + hr = ad->audio_input.capture_client->GetBuffer(&data, &num_frames_available, &flags, NULL, NULL); + ERR_BREAK(hr != S_OK); + + // fixme: Only works for floating point atm + for (int j = 0; j < num_frames_available; j++) { + int32_t l, r; + + if (flags & AUDCLNT_BUFFERFLAGS_SILENT) { + l = r = 0; + } else { + if (ad->audio_input.channels == 2) { + l = read_sample(ad->audio_input.format_tag, ad->audio_input.bits_per_sample, data, j * 2); + r = read_sample(ad->audio_input.format_tag, ad->audio_input.bits_per_sample, data, j * 2 + 1); + } else if (ad->audio_input.channels == 1) { + l = r = read_sample(ad->audio_input.format_tag, ad->audio_input.bits_per_sample, data, j); + } else { + l = r = 0; + ERR_PRINT("WASAPI: unsupported channel count in microphone!"); + } + } + + ad->input_buffer_write(l); + ad->input_buffer_write(r); + } + + read_frames += num_frames_available; + + hr = ad->audio_input.capture_client->ReleaseBuffer(num_frames_available); + ERR_BREAK(hr != S_OK); + + hr = ad->audio_input.capture_client->GetNextPacketSize(&packet_length); + ERR_BREAK(hr != S_OK); + } + } + + // If we're using the Default device and it changed finish it so we'll re-init the device + if (ad->audio_input.device_name == "Default" && default_capture_device_changed) { + Error err = ad->finish_capture_device(); + if (err != OK) { + ERR_PRINT("WASAPI: finish_capture_device error"); + } + + default_capture_device_changed = false; + } + + // User selected a new device, finish the current one so we'll init the new device + if (ad->audio_input.device_name != ad->audio_input.new_device) { + ad->audio_input.device_name = ad->audio_input.new_device; + Error err = ad->finish_capture_device(); + if (err != OK) { + ERR_PRINT("WASAPI: finish_capture_device error"); + } + } + + if (!ad->audio_input.audio_client) { + Error err = ad->init_capture_device(true); + if (err == OK) { + ad->capture_start(); + } + } + } + + ad->stop_counting_ticks(); + ad->unlock(); + + // Let the thread rest a while if we haven't read or write anything + if (written_frames == 0 && read_frames == 0) { + OS::get_singleton()->delay_usec(1000); + } } ad->thread_exited = true; @@ -566,12 +748,12 @@ void AudioDriverWASAPI::thread_func(void *p_udata) { void AudioDriverWASAPI::start() { - if (audio_client) { - HRESULT hr = audio_client->Start(); + if (audio_output.audio_client) { + HRESULT hr = audio_output.audio_client->Start(); if (hr != S_OK) { ERR_PRINT("WASAPI: Start failed"); } else { - active = true; + audio_output.active = true; } } } @@ -598,7 +780,8 @@ void AudioDriverWASAPI::finish() { thread = NULL; } - finish_device(); + finish_capture_device(); + finish_render_device(); if (mutex) { memdelete(mutex); @@ -606,30 +789,70 @@ void AudioDriverWASAPI::finish() { } } +Error AudioDriverWASAPI::capture_start() { + + Error err = init_capture_device(); + if (err != OK) { + ERR_PRINT("WASAPI: init_capture_device error"); + return err; + } + + if (audio_input.active == false) { + audio_input.audio_client->Start(); + audio_input.active = true; + + return OK; + } + + return FAILED; +} + +Error AudioDriverWASAPI::capture_stop() { + + if (audio_input.active == true) { + audio_input.audio_client->Stop(); + audio_input.active = false; + + return OK; + } + + return FAILED; +} + +void AudioDriverWASAPI::capture_set_device(const String &p_name) { + + lock(); + audio_input.new_device = p_name; + unlock(); +} + +Array AudioDriverWASAPI::capture_get_device_list() { + + return audio_device_get_list(true); +} + +String AudioDriverWASAPI::capture_get_device() { + + lock(); + String name = audio_input.device_name; + unlock(); + + return name; +} + AudioDriverWASAPI::AudioDriverWASAPI() { - audio_client = NULL; - render_client = NULL; mutex = NULL; thread = NULL; - format_tag = 0; - bits_per_sample = 0; - samples_in.clear(); - buffer_size = 0; channels = 0; - wasapi_channels = 0; mix_rate = 0; buffer_frames = 0; thread_exited = false; exit_thread = false; - active = false; - - device_name = "Default"; - new_device = "Default"; } #endif diff --git a/drivers/wasapi/audio_driver_wasapi.h b/drivers/wasapi/audio_driver_wasapi.h index c97f4c288c..3d94f3ba49 100644 --- a/drivers/wasapi/audio_driver_wasapi.h +++ b/drivers/wasapi/audio_driver_wasapi.h @@ -43,36 +43,63 @@ class AudioDriverWASAPI : public AudioDriver { - HANDLE event; - IAudioClient *audio_client; - IAudioRenderClient *render_client; + class AudioDeviceWASAPI { + public: + IAudioClient *audio_client; + IAudioRenderClient *render_client; + IAudioCaptureClient *capture_client; + bool active; + + WORD format_tag; + WORD bits_per_sample; + unsigned int channels; + unsigned int frame_size; + + String device_name; + String new_device; + + AudioDeviceWASAPI() { + audio_client = NULL; + render_client = NULL; + capture_client = NULL; + active = false; + format_tag = 0; + bits_per_sample = 0; + channels = 0; + frame_size = 0; + device_name = "Default"; + new_device = "Default"; + } + }; + + AudioDeviceWASAPI audio_input; + AudioDeviceWASAPI audio_output; + Mutex *mutex; Thread *thread; - String device_name; - String new_device; - - WORD format_tag; - WORD bits_per_sample; - Vector<int32_t> samples_in; - unsigned int buffer_size; unsigned int channels; - unsigned int wasapi_channels; int mix_rate; int buffer_frames; bool thread_exited; mutable bool exit_thread; - bool active; - _FORCE_INLINE_ void write_sample(AudioDriverWASAPI *ad, BYTE *buffer, int i, int32_t sample); + static _FORCE_INLINE_ void write_sample(WORD format_tag, int bits_per_sample, BYTE *buffer, int i, int32_t sample); + static _FORCE_INLINE_ int32_t read_sample(WORD format_tag, int bits_per_sample, BYTE *buffer, int i); static void thread_func(void *p_udata); - Error init_device(bool reinit = false); - Error finish_device(); - Error reopen(); + Error init_render_device(bool reinit = false); + Error init_capture_device(bool reinit = false); + + Error finish_render_device(); + Error finish_capture_device(); + + Error audio_device_init(AudioDeviceWASAPI *p_device, bool p_capture, bool reinit); + Error audio_device_finish(AudioDeviceWASAPI *p_device); + Array audio_device_get_list(bool p_capture); public: virtual const char *get_name() const { @@ -90,6 +117,12 @@ public: virtual void unlock(); virtual void finish(); + virtual Error capture_start(); + virtual Error capture_stop(); + virtual Array capture_get_device_list(); + virtual void capture_set_device(const String &p_name); + virtual String capture_get_device(); + AudioDriverWASAPI(); }; diff --git a/drivers/windows/file_access_windows.cpp b/drivers/windows/file_access_windows.cpp index aa0fd34e0a..ea194e5eae 100644 --- a/drivers/windows/file_access_windows.cpp +++ b/drivers/windows/file_access_windows.cpp @@ -141,9 +141,9 @@ void FileAccessWindows::close() { bool rename_error = true; int attempts = 4; while (rename_error && attempts) { - // This workaround of trying multiple times is added to deal with paranoid Windows - // antiviruses that love reading just written files even if they are not executable, thus - // locking the file and preventing renaming from happening. + // This workaround of trying multiple times is added to deal with paranoid Windows + // antiviruses that love reading just written files even if they are not executable, thus + // locking the file and preventing renaming from happening. #ifdef UWP_ENABLED // UWP has no PathFileExists, so we check attributes instead diff --git a/drivers/winmidi/SCsub b/drivers/winmidi/SCsub new file mode 100644 index 0000000000..233593b0f9 --- /dev/null +++ b/drivers/winmidi/SCsub @@ -0,0 +1,8 @@ +#!/usr/bin/env python + +Import('env') + +# Driver source files +env.add_source_files(env.drivers_sources, "*.cpp") + +Export('env') diff --git a/drivers/winmidi/win_midi.cpp b/drivers/winmidi/win_midi.cpp new file mode 100644 index 0000000000..6da6e31b2b --- /dev/null +++ b/drivers/winmidi/win_midi.cpp @@ -0,0 +1,100 @@ +/*************************************************************************/ +/* win_midi.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifdef WINMIDI_ENABLED + +#include "win_midi.h" +#include "print_string.h" + +void MIDIDriverWinMidi::read(HMIDIIN hMidiIn, UINT wMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2) { + + if (wMsg == MIM_DATA) { + receive_input_packet((uint64_t)dwParam2, (uint8_t *)&dwParam1, 3); + } +} + +Error MIDIDriverWinMidi::open() { + + for (UINT i = 0; i < midiInGetNumDevs(); i++) { + HMIDIIN midi_in; + + MMRESULT res = midiInOpen(&midi_in, i, (DWORD_PTR)read, (DWORD_PTR)this, CALLBACK_FUNCTION); + if (res == MMSYSERR_NOERROR) { + midiInStart(midi_in); + connected_sources.insert(i, midi_in); + } else { + char err[256]; + midiInGetErrorText(res, err, 256); + ERR_PRINTS("midiInOpen error: " + String(err)); + } + } + + return OK; +} + +PoolStringArray MIDIDriverWinMidi::get_connected_inputs() { + + PoolStringArray list; + + for (int i = 0; i < connected_sources.size(); i++) { + HMIDIIN midi_in = connected_sources[i]; + UINT id = 0; + MMRESULT res = midiInGetID(midi_in, &id); + if (res == MMSYSERR_NOERROR) { + MIDIINCAPS caps; + res = midiInGetDevCaps(i, &caps, sizeof(MIDIINCAPS)); + if (res == MMSYSERR_NOERROR) { + list.push_back(caps.szPname); + } + } + } + + return list; +} + +void MIDIDriverWinMidi::close() { + + for (int i = 0; i < connected_sources.size(); i++) { + HMIDIIN midi_in = connected_sources[i]; + midiInStop(midi_in); + midiInClose(midi_in); + } + connected_sources.clear(); +} + +MIDIDriverWinMidi::MIDIDriverWinMidi() { +} + +MIDIDriverWinMidi::~MIDIDriverWinMidi() { + + close(); +} + +#endif diff --git a/drivers/winmidi/win_midi.h b/drivers/winmidi/win_midi.h new file mode 100644 index 0000000000..1cf9b19b5d --- /dev/null +++ b/drivers/winmidi/win_midi.h @@ -0,0 +1,61 @@ +/*************************************************************************/ +/* win_midi.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifdef WINMIDI_ENABLED + +#ifndef WIN_MIDI_H +#define WIN_MIDI_H + +#include <stdio.h> +#include <windows.h> + +#include <mmsystem.h> + +#include "core/vector.h" +#include "os/midi_driver.h" + +class MIDIDriverWinMidi : public MIDIDriver { + + Vector<HMIDIIN> connected_sources; + + static void CALLBACK read(HMIDIIN hMidiIn, UINT wMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2); + +public: + virtual Error open(); + virtual void close(); + + virtual PoolStringArray get_connected_inputs(); + + MIDIDriverWinMidi(); + virtual ~MIDIDriverWinMidi(); +}; + +#endif +#endif diff --git a/drivers/xaudio2/audio_driver_xaudio2.cpp b/drivers/xaudio2/audio_driver_xaudio2.cpp index 6675459313..a1002ef4f9 100644 --- a/drivers/xaudio2/audio_driver_xaudio2.cpp +++ b/drivers/xaudio2/audio_driver_xaudio2.cpp @@ -50,7 +50,7 @@ Error AudioDriverXAudio2::init() { speaker_mode = SPEAKER_MODE_STEREO; channels = 2; - int latency = GLOBAL_DEF("audio/output_latency", 25); + int latency = GLOBAL_DEF_RST("audio/output_latency", 25); buffer_size = closest_power_of_2(latency * mix_rate / 1000); samples_in = memnew_arr(int32_t, buffer_size * channels); @@ -97,8 +97,6 @@ void AudioDriverXAudio2::thread_func(void *p_udata) { AudioDriverXAudio2 *ad = (AudioDriverXAudio2 *)p_udata; - uint64_t usdelay = (ad->buffer_size / float(ad->mix_rate)) * 1000000; - while (!ad->exit_thread) { if (!ad->active) { |