Merge pull request #69568 from timothyqiu/3.x-cherrypicks
Cherry-picks for the 3.x branch (future 3.6) - 8th batch
This commit is contained in:
commit
6a99678bc6
19 changed files with 245 additions and 88 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -154,6 +154,7 @@ gmon.out
|
|||
|
||||
# Jetbrains IDEs
|
||||
.idea/
|
||||
.fleet/
|
||||
|
||||
# Kate
|
||||
*.kate-swp
|
||||
|
|
|
@ -88,7 +88,11 @@ public:
|
|||
num_items++;
|
||||
return id;
|
||||
}
|
||||
#ifdef DEV_ENABLED
|
||||
return -1;
|
||||
#else
|
||||
ERR_FAIL_V_MSG(0, "BVH request_item error.");
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -353,6 +353,7 @@
|
|||
<method name="sort">
|
||||
<description>
|
||||
Sorts the array.
|
||||
[b]Note:[/b] The sorting algorithm used is not [url=https://en.wikipedia.org/wiki/Sorting_algorithm#Stability]stable[/url]. This means that values considered equal may have their order changed when using [method sort].
|
||||
[b]Note:[/b] Strings are sorted in alphabetical order (as opposed to natural order). This may lead to unexpected behavior when sorting an array of strings ending with a sequence of numbers. Consider the following example:
|
||||
[codeblock]
|
||||
var strings = ["string1", "string2", "string10", "string11"]
|
||||
|
@ -367,7 +368,8 @@
|
|||
<description>
|
||||
Sorts the array using a custom method. The arguments are an object that holds the method and the name of such method. The custom method receives two arguments (a pair of elements from the array) and must return either [code]true[/code] or [code]false[/code].
|
||||
For two elements [code]a[/code] and [code]b[/code], if the given method returns [code]true[/code], element [code]b[/code] will be after element [code]a[/code] in the array.
|
||||
[b]Note:[/b] You cannot randomize the return value as the heapsort algorithm expects a deterministic result. Doing so will result in unexpected behavior.
|
||||
[b]Note:[/b] The sorting algorithm used is not [url=https://en.wikipedia.org/wiki/Sorting_algorithm#Stability]stable[/url]. This means that values considered equal may have their order changed when using [method sort_custom].
|
||||
[b]Note:[/b] You cannot randomize the return value as the heapsort algorithm expects a deterministic result. Randomizing the return value will result in unexpected behavior.
|
||||
[codeblock]
|
||||
class MyCustomSorter:
|
||||
static func sort_ascending(a, b):
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
print("Hello from the Godot Editor!")
|
||||
[/codeblock]
|
||||
[b]Note:[/b] The script is run in the Editor context, which means the output is visible in the console window started with the Editor (stdout) instead of the usual Godot [b]Output[/b] dock.
|
||||
[b]Note:[/b] EditorScript is reference counted, meaning it is destroyed when nothing references it. This can cause errors during asynchronous operations if there are no references to the script.
|
||||
</description>
|
||||
<tutorials>
|
||||
</tutorials>
|
||||
|
|
|
@ -37,85 +37,135 @@
|
|||
|
||||
#include <errno.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
|
||||
case 0xE0: // pitch bend
|
||||
case 0xF2: // song position pointer
|
||||
return 3;
|
||||
MIDIDriverALSAMidi::MessageCategory MIDIDriverALSAMidi::msg_category(uint8_t msg_part) {
|
||||
if (msg_part >= 0xf8) {
|
||||
return MessageCategory::RealTime;
|
||||
} else if (msg_part >= 0xf0) {
|
||||
// System Exclusive begin/end are specified as System Common Category messages,
|
||||
// but we separate them here and give them their own categories as their
|
||||
// behaviour is significantly different.
|
||||
if (msg_part == 0xf0) {
|
||||
return MessageCategory::SysExBegin;
|
||||
} else if (msg_part == 0xf7) {
|
||||
return MessageCategory::SysExEnd;
|
||||
}
|
||||
return MessageCategory::SystemCommon;
|
||||
} else if (msg_part >= 0x80) {
|
||||
return MessageCategory::Voice;
|
||||
}
|
||||
return MessageCategory::Data;
|
||||
}
|
||||
|
||||
case 0xC0: // patch change
|
||||
case 0xD0: // channel pressure
|
||||
case 0xF1: // time code quarter frame
|
||||
case 0xF3: // song select
|
||||
size_t MIDIDriverALSAMidi::msg_expected_data(uint8_t status_byte) {
|
||||
if (msg_category(status_byte) == MessageCategory::Voice) {
|
||||
// Voice messages have a channel number in the status byte, mask it out.
|
||||
status_byte &= 0xf0;
|
||||
}
|
||||
|
||||
switch (status_byte) {
|
||||
case 0x80: // Note Off
|
||||
case 0x90: // Note On
|
||||
case 0xA0: // Polyphonic Key Pressure (Aftertouch)
|
||||
case 0xB0: // Control Change (CC)
|
||||
case 0xE0: // Pitch Bend Change
|
||||
case 0xF2: // Song Position Pointer
|
||||
return 2;
|
||||
|
||||
case 0xF0: // SysEx start
|
||||
case 0xF4: // reserved
|
||||
case 0xF5: // reserved
|
||||
case 0xF6: // tune request
|
||||
case 0xF7: // SysEx end
|
||||
case 0xF8: // timing clock
|
||||
case 0xF9: // reserved
|
||||
case 0xFA: // start
|
||||
case 0xFB: // continue
|
||||
case 0xFC: // stop
|
||||
case 0xFD: // reserved
|
||||
case 0xFE: // active sensing
|
||||
case 0xFF: // reset
|
||||
case 0xC0: // Program Change
|
||||
case 0xD0: // Channel Pressure (Aftertouch)
|
||||
case 0xF1: // MIDI Time Code Quarter Frame
|
||||
case 0xF3: // Song Select
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 256;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void MIDIDriverALSAMidi::InputConnection::parse_byte(uint8_t byte, MIDIDriverALSAMidi &driver,
|
||||
uint64_t timestamp) {
|
||||
switch (msg_category(byte)) {
|
||||
case MessageCategory::RealTime:
|
||||
// Real-Time messages are single byte messages that can
|
||||
// occur at any point.
|
||||
// We pass them straight through.
|
||||
driver.receive_input_packet(timestamp, &byte, 1);
|
||||
break;
|
||||
|
||||
case MessageCategory::Data:
|
||||
// We don't currently forward System Exclusive messages so skip their data.
|
||||
// Collect any expected data for other message types.
|
||||
if (!skipping_sys_ex && expected_data > received_data) {
|
||||
buffer[received_data + 1] = byte;
|
||||
received_data++;
|
||||
|
||||
// Forward a complete message and reset relevant state.
|
||||
if (received_data == expected_data) {
|
||||
driver.receive_input_packet(timestamp, buffer, received_data + 1);
|
||||
received_data = 0;
|
||||
|
||||
if (msg_category(buffer[0]) != MessageCategory::Voice) {
|
||||
// Voice Category messages can be sent with "running status".
|
||||
// This means they don't resend the status byte until it changes.
|
||||
// For other categories, we reset expected data, to require a new status byte.
|
||||
expected_data = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case MessageCategory::SysExBegin:
|
||||
buffer[0] = byte;
|
||||
skipping_sys_ex = true;
|
||||
break;
|
||||
|
||||
case MessageCategory::SysExEnd:
|
||||
expected_data = 0;
|
||||
skipping_sys_ex = false;
|
||||
break;
|
||||
|
||||
case MessageCategory::Voice:
|
||||
case MessageCategory::SystemCommon:
|
||||
buffer[0] = byte;
|
||||
received_data = 0;
|
||||
expected_data = msg_expected_data(byte);
|
||||
skipping_sys_ex = false;
|
||||
if (expected_data == 0) {
|
||||
driver.receive_input_packet(timestamp, &byte, 1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int MIDIDriverALSAMidi::InputConnection::read_in(MIDIDriverALSAMidi &driver, uint64_t timestamp) {
|
||||
int ret;
|
||||
do {
|
||||
uint8_t byte = 0;
|
||||
ret = snd_rawmidi_read(rawmidi_ptr, &byte, 1);
|
||||
|
||||
if (ret < 0) {
|
||||
if (ret != -EAGAIN) {
|
||||
ERR_PRINT("snd_rawmidi_read error: " + String(snd_strerror(ret)));
|
||||
}
|
||||
} else {
|
||||
parse_byte(byte, driver, timestamp);
|
||||
}
|
||||
} while (ret > 0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
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.is_set()) {
|
||||
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_PRINT("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);
|
||||
// After a SysEx start, all bytes are data until a SysEx end, so
|
||||
// we're going to end the command at the SES, and let the common
|
||||
// driver ignore the following data bytes.
|
||||
}
|
||||
InputConnection *connections = md->connected_inputs.ptrw();
|
||||
size_t connection_count = md->connected_inputs.size();
|
||||
|
||||
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);
|
||||
for (size_t i = 0; i < connection_count; i++) {
|
||||
connections[i].read_in(*md, timestamp);
|
||||
}
|
||||
|
||||
md->unlock();
|
||||
|
@ -139,7 +189,7 @@ Error MIDIDriverALSAMidi::open() {
|
|||
snd_rawmidi_t *midi_in;
|
||||
int ret = snd_rawmidi_open(&midi_in, nullptr, name, SND_RAWMIDI_NONBLOCK);
|
||||
if (ret >= 0) {
|
||||
connected_inputs.insert(i++, midi_in);
|
||||
connected_inputs.insert(i++, InputConnection(midi_in));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -160,7 +210,7 @@ void MIDIDriverALSAMidi::close() {
|
|||
thread.wait_to_finish();
|
||||
|
||||
for (int i = 0; i < connected_inputs.size(); i++) {
|
||||
snd_rawmidi_t *midi_in = connected_inputs[i];
|
||||
snd_rawmidi_t *midi_in = connected_inputs[i].rawmidi_ptr;
|
||||
snd_rawmidi_close(midi_in);
|
||||
}
|
||||
connected_inputs.clear();
|
||||
|
@ -179,7 +229,7 @@ PoolStringArray MIDIDriverALSAMidi::get_connected_inputs() {
|
|||
|
||||
lock();
|
||||
for (int i = 0; i < connected_inputs.size(); i++) {
|
||||
snd_rawmidi_t *midi_in = connected_inputs[i];
|
||||
snd_rawmidi_t *midi_in = connected_inputs[i].rawmidi_ptr;
|
||||
snd_rawmidi_info_t *info;
|
||||
|
||||
snd_rawmidi_info_malloc(&info);
|
||||
|
|
|
@ -46,12 +46,48 @@ class MIDIDriverALSAMidi : public MIDIDriver {
|
|||
Thread thread;
|
||||
Mutex mutex;
|
||||
|
||||
Vector<snd_rawmidi_t *> connected_inputs;
|
||||
class InputConnection {
|
||||
public:
|
||||
InputConnection() = default;
|
||||
InputConnection(snd_rawmidi_t *midi_in) :
|
||||
rawmidi_ptr{ midi_in } {}
|
||||
|
||||
// Read in and parse available data, forwarding any complete messages through the driver.
|
||||
int read_in(MIDIDriverALSAMidi &driver, uint64_t timestamp);
|
||||
|
||||
snd_rawmidi_t *rawmidi_ptr = nullptr;
|
||||
|
||||
private:
|
||||
static const size_t MSG_BUFFER_SIZE = 3;
|
||||
uint8_t buffer[MSG_BUFFER_SIZE] = { 0 };
|
||||
size_t expected_data = 0;
|
||||
size_t received_data = 0;
|
||||
bool skipping_sys_ex = false;
|
||||
void parse_byte(uint8_t byte, MIDIDriverALSAMidi &driver, uint64_t timestamp);
|
||||
};
|
||||
|
||||
Vector<InputConnection> connected_inputs;
|
||||
|
||||
SafeFlag exit_thread;
|
||||
|
||||
static void thread_func(void *p_udata);
|
||||
|
||||
enum class MessageCategory {
|
||||
Data,
|
||||
Voice,
|
||||
SysExBegin,
|
||||
SystemCommon, // excluding System Exclusive Begin/End
|
||||
SysExEnd,
|
||||
RealTime,
|
||||
};
|
||||
|
||||
// If the passed byte is a status byte, return the associated message category,
|
||||
// else return MessageCategory::Data.
|
||||
static MessageCategory msg_category(uint8_t msg_part);
|
||||
|
||||
// Return the number of data bytes expected for the provided status byte.
|
||||
static size_t msg_expected_data(uint8_t status_byte);
|
||||
|
||||
void lock() const;
|
||||
void unlock() const;
|
||||
|
||||
|
|
|
@ -51,6 +51,7 @@ void EditorResourcePicker::_update_resource() {
|
|||
if (edited_resource == RES()) {
|
||||
assign_button->set_icon(Ref<Texture>());
|
||||
assign_button->set_text(TTR("[empty]"));
|
||||
assign_button->set_tooltip("");
|
||||
} else {
|
||||
assign_button->set_icon(EditorNode::get_singleton()->get_object_icon(edited_resource.operator->(), "Object"));
|
||||
|
||||
|
@ -58,14 +59,15 @@ void EditorResourcePicker::_update_resource() {
|
|||
assign_button->set_text(edited_resource->get_name());
|
||||
} else if (edited_resource->get_path().is_resource_file()) {
|
||||
assign_button->set_text(edited_resource->get_path().get_file());
|
||||
assign_button->set_tooltip(edited_resource->get_path());
|
||||
} else {
|
||||
assign_button->set_text(edited_resource->get_class());
|
||||
}
|
||||
|
||||
String resource_path;
|
||||
if (edited_resource->get_path().is_resource_file()) {
|
||||
assign_button->set_tooltip(edited_resource->get_path());
|
||||
resource_path = edited_resource->get_path() + "\n";
|
||||
}
|
||||
assign_button->set_tooltip(resource_path + TTR("Type:") + " " + edited_resource->get_class());
|
||||
|
||||
// Preview will override the above, so called at the end.
|
||||
EditorResourcePreview::get_singleton()->queue_edited_resource_preview(edited_resource, this, "_update_resource_preview", edited_resource->get_instance_id());
|
||||
|
@ -520,6 +522,8 @@ void EditorResourcePicker::_get_allowed_types(bool p_with_convert, Set<String> *
|
|||
p_vector->insert("Texture");
|
||||
} else if (base == "ShaderMaterial") {
|
||||
p_vector->insert("Shader");
|
||||
} else if (base == "Texture") {
|
||||
p_vector->insert("Image");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -638,18 +642,35 @@ void EditorResourcePicker::drop_data_fw(const Point2 &p_point, const Variant &p_
|
|||
String at = E->get().strip_edges();
|
||||
|
||||
if (at == "SpatialMaterial" && ClassDB::is_parent_class(dropped_resource->get_class(), "Texture")) {
|
||||
Ref<SpatialMaterial> mat = memnew(SpatialMaterial);
|
||||
// Use existing resource if possible and only replace its data.
|
||||
Ref<SpatialMaterial> mat = edited_resource;
|
||||
if (mat.is_null()) {
|
||||
mat.instance();
|
||||
}
|
||||
mat->set_texture(SpatialMaterial::TextureParam::TEXTURE_ALBEDO, dropped_resource);
|
||||
dropped_resource = mat;
|
||||
break;
|
||||
}
|
||||
|
||||
if (at == "ShaderMaterial" && ClassDB::is_parent_class(dropped_resource->get_class(), "Shader")) {
|
||||
Ref<ShaderMaterial> mat = memnew(ShaderMaterial);
|
||||
Ref<ShaderMaterial> mat = edited_resource;
|
||||
if (mat.is_null()) {
|
||||
mat.instance();
|
||||
}
|
||||
mat->set_shader(dropped_resource);
|
||||
dropped_resource = mat;
|
||||
break;
|
||||
}
|
||||
|
||||
if (at == "Texture" && ClassDB::is_parent_class(dropped_resource->get_class(), "Image")) {
|
||||
Ref<ImageTexture> texture = edited_resource;
|
||||
if (texture.is_null()) {
|
||||
texture.instance();
|
||||
}
|
||||
texture->create_from_image(dropped_resource);
|
||||
dropped_resource = texture;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -104,9 +104,10 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s
|
|||
file->get_buffer((uint8_t *)&riff, 4); //RIFF
|
||||
|
||||
if (riff[0] != 'R' || riff[1] != 'I' || riff[2] != 'F' || riff[3] != 'F') {
|
||||
uint64_t length = file->get_len();
|
||||
file->close();
|
||||
memdelete(file);
|
||||
ERR_FAIL_V(ERR_FILE_UNRECOGNIZED);
|
||||
ERR_FAIL_V_MSG(ERR_FILE_UNRECOGNIZED, vformat("Not a WAV file. File should start with 'RIFF', but found '%s', in file of size %d bytes", riff, length));
|
||||
}
|
||||
|
||||
/* GET FILESIZE */
|
||||
|
@ -114,14 +115,15 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s
|
|||
|
||||
/* CHECK WAVE */
|
||||
|
||||
char wave[4];
|
||||
|
||||
file->get_buffer((uint8_t *)&wave, 4); //RIFF
|
||||
char wave[5];
|
||||
wave[4] = 0;
|
||||
file->get_buffer((uint8_t *)&wave, 4); //WAVE
|
||||
|
||||
if (wave[0] != 'W' || wave[1] != 'A' || wave[2] != 'V' || wave[3] != 'E') {
|
||||
uint64_t length = file->get_len();
|
||||
file->close();
|
||||
memdelete(file);
|
||||
ERR_FAIL_V_MSG(ERR_FILE_UNRECOGNIZED, "Not a WAV file (no WAVE RIFF header).");
|
||||
ERR_FAIL_V_MSG(ERR_FILE_UNRECOGNIZED, vformat("Not a WAV file. Header should contain 'WAVE', but found '%s', in file of size %d bytes", wave, length));
|
||||
}
|
||||
|
||||
// Let users override potential loop points from the WAV.
|
||||
|
|
|
@ -208,6 +208,8 @@
|
|||
<members>
|
||||
<member name="buffers" type="Array" setter="set_buffers" getter="get_buffers" default="[ ]">
|
||||
</member>
|
||||
<member name="create_animations" type="bool" setter="set_create_animations" getter="get_create_animations" default="true">
|
||||
</member>
|
||||
<member name="glb_data" type="PoolByteArray" setter="set_glb_data" getter="get_glb_data" default="PoolByteArray( )">
|
||||
</member>
|
||||
<member name="json" type="Dictionary" setter="set_json" getter="get_json" default="{}">
|
||||
|
|
|
@ -81,6 +81,8 @@ void GLTFState::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("set_skeletons", "skeletons"), &GLTFState::set_skeletons);
|
||||
ClassDB::bind_method(D_METHOD("get_skeleton_to_node"), &GLTFState::get_skeleton_to_node);
|
||||
ClassDB::bind_method(D_METHOD("set_skeleton_to_node", "skeleton_to_node"), &GLTFState::set_skeleton_to_node);
|
||||
ClassDB::bind_method(D_METHOD("get_create_animations"), &GLTFState::get_create_animations);
|
||||
ClassDB::bind_method(D_METHOD("set_create_animations", "create_animations"), &GLTFState::set_create_animations);
|
||||
ClassDB::bind_method(D_METHOD("get_animations"), &GLTFState::get_animations);
|
||||
ClassDB::bind_method(D_METHOD("set_animations", "animations"), &GLTFState::set_animations);
|
||||
ClassDB::bind_method(D_METHOD("get_scene_node", "idx"), &GLTFState::get_scene_node);
|
||||
|
@ -108,6 +110,7 @@ void GLTFState::_bind_methods() {
|
|||
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "unique_animation_names", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_unique_animation_names", "get_unique_animation_names"); // Set<String>
|
||||
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "skeletons", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_skeletons", "get_skeletons"); // Vector<Ref<GLTFSkeleton>>
|
||||
ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "skeleton_to_node", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_skeleton_to_node", "get_skeleton_to_node"); // Map<GLTFSkeletonIndex,
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "create_animations"), "set_create_animations", "get_create_animations"); // bool
|
||||
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "animations", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_animations", "get_animations"); // Vector<Ref<GLTFAnimation>>
|
||||
}
|
||||
|
||||
|
@ -295,6 +298,14 @@ void GLTFState::set_skeleton_to_node(Dictionary p_skeleton_to_node) {
|
|||
GLTFTemplateConvert::set_from_dict(skeleton_to_node, p_skeleton_to_node);
|
||||
}
|
||||
|
||||
bool GLTFState::get_create_animations() {
|
||||
return create_animations;
|
||||
}
|
||||
|
||||
void GLTFState::set_create_animations(bool p_create_animations) {
|
||||
create_animations = p_create_animations;
|
||||
}
|
||||
|
||||
Array GLTFState::get_animations() {
|
||||
return GLTFTemplateConvert::to_array(animations);
|
||||
}
|
||||
|
|
|
@ -59,6 +59,7 @@ class GLTFState : public Resource {
|
|||
bool use_khr_texture_transform = false;
|
||||
bool use_legacy_names = false;
|
||||
uint32_t compress_flags = 0;
|
||||
bool create_animations = true;
|
||||
|
||||
Vector<Ref<GLTFNode>> nodes;
|
||||
Vector<Vector<uint8_t>> buffers;
|
||||
|
@ -166,6 +167,9 @@ public:
|
|||
Dictionary get_skeleton_to_node();
|
||||
void set_skeleton_to_node(Dictionary p_skeleton_to_node);
|
||||
|
||||
bool get_create_animations();
|
||||
void set_create_animations(bool p_create_animations);
|
||||
|
||||
Array get_animations();
|
||||
void set_animations(Array p_animations);
|
||||
|
||||
|
|
|
@ -67,6 +67,7 @@ Node *PackedSceneGLTF::import_scene(const String &p_path, uint32_t p_flags,
|
|||
r_state->use_legacy_names =
|
||||
p_flags & EditorSceneImporter::IMPORT_USE_LEGACY_NAMES;
|
||||
r_state->compress_flags = p_compress_flags;
|
||||
r_state->set_create_animations(p_flags & EditorSceneImporter::IMPORT_ANIMATION);
|
||||
|
||||
Ref<GLTFDocument> gltf_document;
|
||||
gltf_document.instance();
|
||||
|
@ -84,7 +85,7 @@ Node *PackedSceneGLTF::import_scene(const String &p_path, uint32_t p_flags,
|
|||
gltf_document->_generate_scene_node(r_state, root, root, r_state->root_nodes[root_i]);
|
||||
}
|
||||
gltf_document->_process_mesh_instances(r_state, root);
|
||||
if (r_state->animations.size()) {
|
||||
if (r_state->get_create_animations() && r_state->animations.size()) {
|
||||
AnimationPlayer *ap = memnew(AnimationPlayer);
|
||||
root->add_child(ap);
|
||||
ap->set_owner(root);
|
||||
|
|
|
@ -100,7 +100,7 @@ Error ImageLoaderTGA::convert_to_image(Ref<Image> p_image, const uint8_t *p_buff
|
|||
uint32_t width = p_header.image_width;
|
||||
uint32_t height = p_header.image_height;
|
||||
tga_origin_e origin = static_cast<tga_origin_e>((p_header.image_descriptor & TGA_ORIGIN_MASK) >> TGA_ORIGIN_SHIFT);
|
||||
|
||||
uint8_t alpha_bits = p_header.image_descriptor & TGA_IMAGE_DESCRIPTOR_ALPHA_MASK;
|
||||
uint32_t x_start;
|
||||
int32_t x_step;
|
||||
uint32_t x_end;
|
||||
|
@ -184,6 +184,27 @@ Error ImageLoaderTGA::convert_to_image(Ref<Image> p_image, const uint8_t *p_buff
|
|||
y += y_step;
|
||||
}
|
||||
}
|
||||
} else if (p_header.pixel_depth == 16) {
|
||||
while (y != y_end) {
|
||||
while (x != x_end) {
|
||||
if (i + 1 >= p_input_size) {
|
||||
return ERR_PARSE_ERROR;
|
||||
}
|
||||
|
||||
// Always stored as RGBA5551
|
||||
uint8_t r = (p_buffer[i + 1] & 0x7c) << 1;
|
||||
uint8_t g = ((p_buffer[i + 1] & 0x03) << 6) | ((p_buffer[i + 0] & 0xe0) >> 2);
|
||||
uint8_t b = (p_buffer[i + 0] & 0x1f) << 3;
|
||||
uint8_t a = (p_buffer[i + 1] & 0x80) ? 0xff : 0;
|
||||
|
||||
TGA_PUT_PIXEL(r, g, b, alpha_bits ? a : 0xff);
|
||||
|
||||
x += x_step;
|
||||
i += 2;
|
||||
}
|
||||
x = x_start;
|
||||
y += y_step;
|
||||
}
|
||||
} else if (p_header.pixel_depth == 24) {
|
||||
while (y != y_end) {
|
||||
while (x != x_end) {
|
||||
|
@ -286,7 +307,7 @@ Error ImageLoaderTGA::load_image(Ref<Image> p_image, FileAccess *f, bool p_force
|
|||
err = FAILED;
|
||||
}
|
||||
|
||||
if (!(tga_header.pixel_depth == 8 || tga_header.pixel_depth == 24 || tga_header.pixel_depth == 32)) {
|
||||
if (!(tga_header.pixel_depth == 8 || tga_header.pixel_depth == 16 || tga_header.pixel_depth == 24 || tga_header.pixel_depth == 32)) {
|
||||
err = FAILED;
|
||||
}
|
||||
|
||||
|
|
|
@ -36,6 +36,8 @@
|
|||
/**
|
||||
@author SaracenOne
|
||||
*/
|
||||
#define TGA_IMAGE_DESCRIPTOR_ALPHA_MASK 0xf
|
||||
|
||||
class ImageLoaderTGA : public ImageFormatLoader {
|
||||
enum tga_type_e {
|
||||
TGA_TYPE_NO_DATA = 0,
|
||||
|
|
|
@ -126,11 +126,10 @@ def configure(env):
|
|||
# `-O2` is more friendly to debuggers than `-O3`, leading to better crash backtraces
|
||||
# when using `target=release_debug`.
|
||||
opt = "-O3" if env["target"] == "release" else "-O2"
|
||||
env.Append(CCFLAGS=[opt, "-fomit-frame-pointer"])
|
||||
env.Append(CCFLAGS=[opt])
|
||||
elif env["optimize"] == "size": # optimize for size
|
||||
env.Append(CCFLAGS=["-Oz"])
|
||||
env.Append(CPPDEFINES=["NDEBUG"])
|
||||
env.Append(CCFLAGS=["-ftree-vectorize"])
|
||||
elif env["target"] == "debug":
|
||||
env.Append(LINKFLAGS=["-O0"])
|
||||
env.Append(CCFLAGS=["-O0", "-g", "-fno-limit-debug-info"])
|
||||
|
|
|
@ -54,10 +54,10 @@ def configure(env):
|
|||
# `-O2` is more friendly to debuggers than `-O3`, leading to better crash backtraces
|
||||
# when using `target=release_debug`.
|
||||
opt = "-O3" if env["target"] == "release" else "-O2"
|
||||
env.Append(CCFLAGS=[opt, "-ftree-vectorize", "-fomit-frame-pointer"])
|
||||
env.Append(CCFLAGS=[opt])
|
||||
env.Append(LINKFLAGS=[opt])
|
||||
elif env["optimize"] == "size": # optimize for size
|
||||
env.Append(CCFLAGS=["-Os", "-ftree-vectorize"])
|
||||
env.Append(CCFLAGS=["-Os"])
|
||||
env.Append(LINKFLAGS=["-Os"])
|
||||
|
||||
elif env["target"] == "debug":
|
||||
|
|
|
@ -43,9 +43,9 @@ def configure(env):
|
|||
|
||||
if env["target"] == "release":
|
||||
if env["optimize"] == "speed": # optimize for speed (default)
|
||||
env.Prepend(CCFLAGS=["-O3", "-fomit-frame-pointer", "-ftree-vectorize"])
|
||||
env.Prepend(CCFLAGS=["-O3"])
|
||||
elif env["optimize"] == "size": # optimize for size
|
||||
env.Prepend(CCFLAGS=["-Os", "-ftree-vectorize"])
|
||||
env.Prepend(CCFLAGS=["-Os"])
|
||||
if env["arch"] != "arm64":
|
||||
env.Prepend(CCFLAGS=["-msse2"])
|
||||
|
||||
|
|
|
@ -1337,7 +1337,6 @@ float AnimationPlayer::get_current_animation_length() const {
|
|||
|
||||
void AnimationPlayer::_animation_changed() {
|
||||
clear_caches();
|
||||
emit_signal("caches_cleared");
|
||||
if (is_playing()) {
|
||||
playback.seeked = true; //need to restart stuff, like audio
|
||||
}
|
||||
|
@ -1376,6 +1375,8 @@ void AnimationPlayer::clear_caches() {
|
|||
cache_update_size = 0;
|
||||
cache_update_prop_size = 0;
|
||||
cache_update_bezier_size = 0;
|
||||
|
||||
emit_signal("caches_cleared");
|
||||
}
|
||||
|
||||
void AnimationPlayer::set_active(bool p_active) {
|
||||
|
|
|
@ -713,7 +713,6 @@ void AnimationTree::_clear_caches() {
|
|||
memdelete(track_cache[*K]);
|
||||
}
|
||||
playing_caches.clear();
|
||||
|
||||
track_cache.clear();
|
||||
cache_valid = false;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue