Merge pull request #69400 from akien-mga/3.x-cherrypicks
Cherry-picks for the 3.x branch (future 3.6) - 6th batch
This commit is contained in:
commit
87296b3ea5
|
@ -3098,6 +3098,7 @@ Ref<Image> Image::rgbe_to_srgb() {
|
|||
void Image::bumpmap_to_normalmap(float bump_scale) {
|
||||
ERR_FAIL_COND(!_can_modify(format));
|
||||
ERR_FAIL_COND_MSG(write_lock.ptr(), "Cannot modify image when it is locked.");
|
||||
clear_mipmaps();
|
||||
convert(Image::FORMAT_RF);
|
||||
|
||||
PoolVector<uint8_t> result_image; //rgba output
|
||||
|
|
|
@ -301,7 +301,7 @@ Error DirAccess::copy(String p_from, String p_to, int p_chmod_flags) {
|
|||
const size_t copy_buffer_limit = 65536; // 64 KB
|
||||
|
||||
fsrc->seek_end(0);
|
||||
int size = fsrc->get_position();
|
||||
uint64_t size = fsrc->get_position();
|
||||
fsrc->seek(0);
|
||||
err = OK;
|
||||
size_t buffer_size = MIN(size * sizeof(uint8_t), copy_buffer_limit);
|
||||
|
|
|
@ -87,11 +87,6 @@ void MIDIDriver::receive_input_packet(uint64_t timestamp, uint8_t *data, uint32_
|
|||
if (length >= 2 + param_position) {
|
||||
event->set_pitch(data[param_position]);
|
||||
event->set_velocity(data[param_position + 1]);
|
||||
|
||||
if (event->get_message() == MIDI_MESSAGE_NOTE_ON && event->get_velocity() == 0) {
|
||||
// https://www.midi.org/forum/228-writing-midi-software-send-note-off,-or-zero-velocity-note-on
|
||||
event->set_message(MIDI_MESSAGE_NOTE_OFF);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
|
@ -1049,6 +1049,7 @@ ProjectSettings::ProjectSettings() {
|
|||
// Initialization of engine variables should be done in the setup() method,
|
||||
// so that the values can be overridden from project.godot or project.binary.
|
||||
|
||||
CRASH_COND_MSG(singleton != nullptr, "Instantiating a new ProjectSettings singleton is not supported.");
|
||||
singleton = this;
|
||||
last_order = NO_BUILTIN_ORDER_BASE;
|
||||
last_builtin_order = 0;
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
<method name="get_frames_available" qualifiers="const">
|
||||
<return type="int" />
|
||||
<description>
|
||||
Returns the number of audio data frames left to play. If this returned number reaches [code]0[/code], the audio will stop playing until frames are added again. Therefore, make sure your script can always generate and push new audio frames fast enough to avoid audio cracking.
|
||||
Returns the number of frames that can be pushed to the audio sample data buffer without overflowing it. If the result is [code]0[/code], the buffer is full.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_skips" qualifiers="const">
|
||||
|
|
|
@ -42,7 +42,7 @@
|
|||
The pressure of the MIDI signal. This value ranges from 0 to 127. For many devices, this value is always zero.
|
||||
</member>
|
||||
<member name="velocity" type="int" setter="set_velocity" getter="get_velocity" default="0">
|
||||
The velocity of the MIDI signal. This value ranges from 0 to 127. For a piano, this corresponds to how quickly the key was pressed, and is rarely above about 110 in practice.
|
||||
The velocity of the MIDI signal. This value ranges from 0 to 127. For a piano, this corresponds to how quickly the key was pressed, and is rarely above about 110 in practice. Note that some MIDI devices may send a [constant MIDI_MESSAGE_NOTE_ON] message with zero velocity and expect this to be treated the same as a [constant MIDI_MESSAGE_NOTE_OFF] message, but device implementations vary so Godot reports event data exactly as received.
|
||||
</member>
|
||||
</members>
|
||||
<constants>
|
||||
|
|
|
@ -493,6 +493,7 @@
|
|||
<argument index="0" name="id" type="int" />
|
||||
<description>
|
||||
Emitted when an item of some [code]id[/code] is pressed or its accelerator is activated.
|
||||
[b]Note:[/b] If [code]id[/code] is negative (either explicitly or due to overflow), this will return the correponding index instead.
|
||||
</description>
|
||||
</signal>
|
||||
<signal name="index_pressed">
|
||||
|
|
|
@ -237,10 +237,10 @@ vec4 apply_fxaa(vec4 color, vec2 uv_interp, vec2 pixel_size) {
|
|||
const float FXAA_SPAN_MAX = 8.0;
|
||||
const vec3 luma = vec3(0.299, 0.587, 0.114);
|
||||
|
||||
vec4 rgbNW = texture2DLod(source, uv_interp + vec2(-1.0, -1.0) * pixel_size, 0.0);
|
||||
vec4 rgbNE = texture2DLod(source, uv_interp + vec2(1.0, -1.0) * pixel_size, 0.0);
|
||||
vec4 rgbSW = texture2DLod(source, uv_interp + vec2(-1.0, 1.0) * pixel_size, 0.0);
|
||||
vec4 rgbSE = texture2DLod(source, uv_interp + vec2(1.0, 1.0) * pixel_size, 0.0);
|
||||
vec4 rgbNW = textureLod(source, uv_interp + vec2(-0.5, -0.5) * pixel_size, 0.0);
|
||||
vec4 rgbNE = textureLod(source, uv_interp + vec2(0.5, -0.5) * pixel_size, 0.0);
|
||||
vec4 rgbSW = textureLod(source, uv_interp + vec2(-0.5, 0.5) * pixel_size, 0.0);
|
||||
vec4 rgbSE = textureLod(source, uv_interp + vec2(0.5, 0.5) * pixel_size, 0.0);
|
||||
vec3 rgbM = color.rgb;
|
||||
|
||||
#ifdef DISABLE_ALPHA
|
||||
|
|
|
@ -325,10 +325,10 @@ vec4 apply_fxaa(vec4 color, float exposure, vec2 uv_interp, vec2 pixel_size) {
|
|||
const float FXAA_SPAN_MAX = 8.0;
|
||||
const vec3 luma = vec3(0.299, 0.587, 0.114);
|
||||
|
||||
vec4 rgbNW = textureLod(source, uv_interp + vec2(-1.0, -1.0) * pixel_size, 0.0);
|
||||
vec4 rgbNE = textureLod(source, uv_interp + vec2(1.0, -1.0) * pixel_size, 0.0);
|
||||
vec4 rgbSW = textureLod(source, uv_interp + vec2(-1.0, 1.0) * pixel_size, 0.0);
|
||||
vec4 rgbSE = textureLod(source, uv_interp + vec2(1.0, 1.0) * pixel_size, 0.0);
|
||||
vec4 rgbNW = textureLod(source, uv_interp + vec2(-0.5, -0.5) * pixel_size, 0.0);
|
||||
vec4 rgbNE = textureLod(source, uv_interp + vec2(0.5, -0.5) * pixel_size, 0.0);
|
||||
vec4 rgbSW = textureLod(source, uv_interp + vec2(-0.5, 0.5) * pixel_size, 0.0);
|
||||
vec4 rgbSE = textureLod(source, uv_interp + vec2(0.5, 0.5) * pixel_size, 0.0);
|
||||
vec3 rgbM = color.rgb;
|
||||
|
||||
#ifdef DISABLE_ALPHA
|
||||
|
|
|
@ -154,7 +154,7 @@ Error DirAccessWindows::make_dir(String p_dir) {
|
|||
if (p_dir.is_rel_path())
|
||||
p_dir = current_dir.plus_file(p_dir);
|
||||
|
||||
p_dir = p_dir.replace("/", "\\");
|
||||
p_dir = p_dir.simplify_path().replace("/", "\\");
|
||||
|
||||
bool success;
|
||||
int err;
|
||||
|
|
|
@ -845,6 +845,8 @@ void Collada::_parse_curve_geometry(XMLParser &parser, String p_id, String p_nam
|
|||
|
||||
CurveData &curvedata = state.curve_data_map[p_id];
|
||||
curvedata.name = p_name;
|
||||
String closed = parser.get_attribute_value_safe("closed").to_lower();
|
||||
curvedata.closed = closed == "true" || closed == "1";
|
||||
|
||||
COLLADA_PRINT("curve name: " + p_name);
|
||||
|
||||
|
|
|
@ -549,7 +549,12 @@ bool EditorFileSystem::_update_scan_actions() {
|
|||
if (_test_for_reimport(full_path, false)) {
|
||||
//must reimport
|
||||
reimports.push_back(full_path);
|
||||
reimports.append_array(_get_dependencies(full_path));
|
||||
Vector<String> dependencies = _get_dependencies(full_path);
|
||||
for (int i = 0; i < dependencies.size(); i++) {
|
||||
if (import_extensions.has(dependencies[i].get_extension())) {
|
||||
reimports.push_back(dependencies[i]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//must not reimport, all was good
|
||||
//update modified times, to avoid reimport
|
||||
|
|
|
@ -3834,7 +3834,7 @@ void EditorNode::_quick_opened() {
|
|||
List<String> scene_extensions;
|
||||
ResourceLoader::get_recognized_extensions_for_type("PackedScene", &scene_extensions);
|
||||
|
||||
if (open_scene_dialog || scene_extensions.find(files[i].get_extension())) {
|
||||
if (open_scene_dialog || scene_extensions.find(files[i].get_extension().to_lower())) {
|
||||
open_request(res_path);
|
||||
} else {
|
||||
load_resource(res_path);
|
||||
|
|
|
@ -431,8 +431,7 @@ void FindInFilesDialog::set_find_in_files_mode(FindInFilesMode p_mode) {
|
|||
}
|
||||
|
||||
String FindInFilesDialog::get_search_text() const {
|
||||
String text = _search_text_line_edit->get_text();
|
||||
return text.strip_edges();
|
||||
return _search_text_line_edit->get_text();
|
||||
}
|
||||
|
||||
String FindInFilesDialog::get_replace_text() const {
|
||||
|
|
|
@ -1074,6 +1074,12 @@ Error ColladaImport::_create_resources(Collada::Node *p_node, uint32_t p_use_com
|
|||
c->set_point_tilt(i, tilts->array[i]);
|
||||
}
|
||||
}
|
||||
if (cd.closed && pc > 1) {
|
||||
Vector3 pos = c->get_point_position(0);
|
||||
Vector3 in = c->get_point_in(0);
|
||||
Vector3 out = c->get_point_out(0);
|
||||
c->add_point(pos, in, out, -1);
|
||||
}
|
||||
|
||||
curve_cache[ng->source] = c;
|
||||
path->set_curve(c);
|
||||
|
|
|
@ -1478,16 +1478,17 @@ bool ScriptTextEditor::can_drop_data_fw(const Point2 &p_point, const Variant &p_
|
|||
}
|
||||
|
||||
static Node *_find_script_node(Node *p_edited_scene, Node *p_current_node, const Ref<Script> &script) {
|
||||
if (p_edited_scene != p_current_node && p_current_node->get_owner() != p_edited_scene) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Ref<Script> scr = p_current_node->get_script();
|
||||
|
||||
if (scr.is_valid() && scr == script) {
|
||||
return p_current_node;
|
||||
// Check scripts only for the nodes belonging to the edited scene.
|
||||
if (p_current_node == p_edited_scene || p_current_node->get_owner() == p_edited_scene) {
|
||||
Ref<Script> scr = p_current_node->get_script();
|
||||
if (scr.is_valid() && scr == script) {
|
||||
return p_current_node;
|
||||
}
|
||||
}
|
||||
|
||||
// Traverse all children, even the ones not owned by the edited scene as they
|
||||
// can still have child nodes added within the edited scene and thus owned by
|
||||
// it (e.g. nodes added to subscene's root or to its editable children).
|
||||
for (int i = 0; i < p_current_node->get_child_count(); i++) {
|
||||
Node *n = _find_script_node(p_edited_scene, p_current_node->get_child(i), script);
|
||||
if (n) {
|
||||
|
|
|
@ -4151,7 +4151,8 @@ bool SpatialEditorViewport::can_drop_data_fw(const Point2 &p_point, const Varian
|
|||
ResourceLoader::get_recognized_extensions_for_type("Mesh", &mesh_extensions);
|
||||
|
||||
for (int i = 0; i < files.size(); i++) {
|
||||
if (mesh_extensions.find(files[i].get_extension()) || scene_extensions.find(files[i].get_extension())) {
|
||||
String extension = files[i].get_extension().to_lower();
|
||||
if (mesh_extensions.find(extension) || scene_extensions.find(extension)) {
|
||||
RES res = ResourceLoader::load(files[i]);
|
||||
if (res.is_null()) {
|
||||
continue;
|
||||
|
|
|
@ -67,14 +67,18 @@ int SpriteFramesEditor::_sheet_preview_position_to_frame_index(const Point2 &p_p
|
|||
const Size2i block_size = frame_size + separation;
|
||||
const Point2i position = p_position / sheet_zoom - offset;
|
||||
|
||||
if (position.x % block_size.x > frame_size.x || position.y % block_size.y > frame_size.y) {
|
||||
if (position.x < 0 || position.y < 0) {
|
||||
return -1; // Out of bounds.
|
||||
}
|
||||
|
||||
if (position.x % block_size.x >= frame_size.x || position.y % block_size.y >= frame_size.y) {
|
||||
return -1; // Gap between frames.
|
||||
}
|
||||
|
||||
const Point2i frame = position / block_size;
|
||||
const Size2i frame_count = _get_frame_count();
|
||||
if (frame.x < 0 || frame.y < 0 || frame.x >= frame_count.x || frame.y >= frame_count.y) {
|
||||
return -1; // Out of bound.
|
||||
if (frame.x >= frame_count.x || frame.y >= frame_count.y) {
|
||||
return -1; // Out of bounds.
|
||||
}
|
||||
|
||||
return frame_count.x * frame.y + frame.x;
|
||||
|
|
|
@ -431,17 +431,17 @@ private:
|
|||
return;
|
||||
}
|
||||
|
||||
ProjectSettings *current = memnew(ProjectSettings);
|
||||
|
||||
int err = current->setup(dir2, "");
|
||||
// Load project.godot as ConfigFile to set the new name.
|
||||
ConfigFile cfg;
|
||||
String project_godot = dir2.plus_file("project.godot");
|
||||
Error err = cfg.load(project_godot);
|
||||
if (err != OK) {
|
||||
set_message(vformat(TTR("Couldn't load project.godot in project path (error %d). It may be missing or corrupted."), err), MESSAGE_ERROR);
|
||||
set_message(vformat(TTR("Couldn't load project at '%s' (error %d). It may be missing or corrupted."), project_godot, err), MESSAGE_ERROR);
|
||||
} else {
|
||||
ProjectSettings::CustomMap edited_settings;
|
||||
edited_settings["application/config/name"] = project_name->get_text().strip_edges();
|
||||
|
||||
if (current->save_custom(dir2.plus_file("project.godot"), edited_settings, Vector<String>(), true) != OK) {
|
||||
set_message(TTR("Couldn't edit project.godot in project path."), MESSAGE_ERROR);
|
||||
cfg.set_value("application", "config/name", project_name->get_text().strip_edges());
|
||||
err = cfg.save(project_godot);
|
||||
if (err != OK) {
|
||||
set_message(vformat(TTR("Couldn't save project at '%s' (error %d)."), project_godot, err), MESSAGE_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -689,18 +689,19 @@ public:
|
|||
rasterizer_container->hide();
|
||||
get_ok()->set_disabled(false);
|
||||
|
||||
ProjectSettings *current = memnew(ProjectSettings);
|
||||
|
||||
int err = current->setup(project_path->get_text(), "");
|
||||
// Fetch current name from project.godot to prefill the text input.
|
||||
ConfigFile cfg;
|
||||
String project_godot = project_path->get_text().plus_file("project.godot");
|
||||
Error err = cfg.load(project_godot);
|
||||
if (err != OK) {
|
||||
set_message(vformat(TTR("Couldn't load project.godot in project path (error %d). It may be missing or corrupted."), err), MESSAGE_ERROR);
|
||||
set_message(vformat(TTR("Couldn't load project at '%s' (error %d). It may be missing or corrupted."), project_godot, err), MESSAGE_ERROR);
|
||||
status_rect->show();
|
||||
msg->show();
|
||||
get_ok()->set_disabled(true);
|
||||
} else if (current->has_setting("application/config/name")) {
|
||||
String proj = current->get("application/config/name");
|
||||
project_name->set_text(proj);
|
||||
_text_changed(proj);
|
||||
} else {
|
||||
String cur_name = cfg.get_value("application", "config/name", "");
|
||||
project_name->set_text(cur_name);
|
||||
_text_changed(cur_name);
|
||||
}
|
||||
|
||||
project_name->call_deferred("grab_focus");
|
||||
|
|
|
@ -2288,8 +2288,22 @@ void ScriptEditorDebugger::_item_menu_id_pressed(int p_option) {
|
|||
ti = ti->get_parent();
|
||||
}
|
||||
|
||||
// We only need the first child here (C++ source stack trace).
|
||||
// Find the child with the "C++ Source".
|
||||
// It's not at a fixed position as "C++ Error" may come first.
|
||||
TreeItem *ci = ti->get_children();
|
||||
const String cpp_source = "<" + TTR("C++ Source") + ">";
|
||||
while (ci) {
|
||||
if (ci->get_text(0) == cpp_source) {
|
||||
break;
|
||||
}
|
||||
ci = ci->get_next();
|
||||
}
|
||||
|
||||
if (!ci) {
|
||||
WARN_PRINT("No C++ source reference is available for this error.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse back the `file:line @ method()` string.
|
||||
const Vector<String> file_line_number = ci->get_text(1).split("@")[0].strip_edges().split(":");
|
||||
ERR_FAIL_COND_MSG(file_line_number.size() < 2, "Incorrect C++ source stack trace file:line format (please report).");
|
||||
|
|
|
@ -265,14 +265,21 @@ Error ImageLoaderTGA::load_image(Ref<Image> p_image, FileAccess *f, bool p_force
|
|||
err = FAILED;
|
||||
}
|
||||
|
||||
uint64_t color_map_size;
|
||||
if (has_color_map) {
|
||||
if (tga_header.color_map_length > 256 || (tga_header.color_map_depth != 24) || tga_header.color_map_type != 1) {
|
||||
err = FAILED;
|
||||
}
|
||||
color_map_size = tga_header.color_map_length * (tga_header.color_map_depth >> 3);
|
||||
} else {
|
||||
if (tga_header.color_map_type) {
|
||||
err = FAILED;
|
||||
}
|
||||
color_map_size = 0;
|
||||
}
|
||||
|
||||
if ((src_image_len - f->get_position()) < (tga_header.id_length + color_map_size)) {
|
||||
err = FAILED; // TGA data appears to be truncated (fewer bytes than expected).
|
||||
}
|
||||
|
||||
if (tga_header.image_width <= 0 || tga_header.image_height <= 0) {
|
||||
|
@ -289,7 +296,6 @@ Error ImageLoaderTGA::load_image(Ref<Image> p_image, FileAccess *f, bool p_force
|
|||
PoolVector<uint8_t> palette;
|
||||
|
||||
if (has_color_map) {
|
||||
size_t color_map_size = tga_header.color_map_length * (tga_header.color_map_depth >> 3);
|
||||
err = palette.resize(color_map_size);
|
||||
if (err == OK) {
|
||||
PoolVector<uint8_t>::Write palette_w = palette.write();
|
||||
|
|
|
@ -43,7 +43,6 @@ struct AndroidGodotCodePair {
|
|||
|
||||
static AndroidGodotCodePair android_godot_code_pairs[] = {
|
||||
{ AKEYCODE_UNKNOWN, KEY_UNKNOWN }, // (0) Unknown key code.
|
||||
{ AKEYCODE_HOME, KEY_HOME }, // (3) Home key.
|
||||
{ AKEYCODE_BACK, KEY_BACK }, // (4) Back key.
|
||||
{ AKEYCODE_0, KEY_0 }, // (7) '0' key.
|
||||
{ AKEYCODE_1, KEY_1 }, // (8) '1' key.
|
||||
|
@ -63,6 +62,7 @@ static AndroidGodotCodePair android_godot_code_pairs[] = {
|
|||
{ AKEYCODE_DPAD_RIGHT, KEY_RIGHT }, // (22) Directional Pad Right key.
|
||||
{ AKEYCODE_VOLUME_UP, KEY_VOLUMEUP }, // (24) Volume Up key.
|
||||
{ AKEYCODE_VOLUME_DOWN, KEY_VOLUMEDOWN }, // (25) Volume Down key.
|
||||
{ AKEYCODE_POWER, KEY_STANDBY }, // (26) Power key.
|
||||
{ AKEYCODE_CLEAR, KEY_CLEAR }, // (28) Clear key.
|
||||
{ AKEYCODE_A, KEY_A }, // (29) 'A' key.
|
||||
{ AKEYCODE_B, KEY_B }, // (30) 'B' key.
|
||||
|
@ -98,6 +98,7 @@ static AndroidGodotCodePair android_godot_code_pairs[] = {
|
|||
{ AKEYCODE_SHIFT_RIGHT, KEY_SHIFT }, // (60) Right Shift modifier key.
|
||||
{ AKEYCODE_TAB, KEY_TAB }, // (61) Tab key.
|
||||
{ AKEYCODE_SPACE, KEY_SPACE }, // (62) Space key.
|
||||
{ AKEYCODE_ENVELOPE, KEY_LAUNCHMAIL }, // (65) Envelope special function key.
|
||||
{ AKEYCODE_ENTER, KEY_ENTER }, // (66) Enter key.
|
||||
{ AKEYCODE_DEL, KEY_BACKSPACE }, // (67) Backspace key.
|
||||
{ AKEYCODE_GRAVE, KEY_QUOTELEFT }, // (68) '`' (backtick) key.
|
||||
|
@ -114,6 +115,7 @@ static AndroidGodotCodePair android_godot_code_pairs[] = {
|
|||
{ AKEYCODE_MENU, KEY_MENU }, // (82) Menu key.
|
||||
{ AKEYCODE_SEARCH, KEY_SEARCH }, // (84) Search key.
|
||||
{ AKEYCODE_MEDIA_STOP, KEY_MEDIASTOP }, // (86) Stop media key.
|
||||
{ AKEYCODE_MEDIA_NEXT, KEY_MEDIANEXT }, // (87) Play Next media key.
|
||||
{ AKEYCODE_MEDIA_PREVIOUS, KEY_MEDIAPREVIOUS }, // (88) Play Previous media key.
|
||||
{ AKEYCODE_PAGE_UP, KEY_PAGEUP }, // (92) Page Up key.
|
||||
{ AKEYCODE_PAGE_DOWN, KEY_PAGEDOWN }, // (93) Page Down key.
|
||||
|
@ -127,6 +129,8 @@ static AndroidGodotCodePair android_godot_code_pairs[] = {
|
|||
{ AKEYCODE_META_RIGHT, KEY_META }, // (118) Right Meta modifier key.
|
||||
{ AKEYCODE_SYSRQ, KEY_PRINT }, // (120) System Request / Print Screen key.
|
||||
{ AKEYCODE_BREAK, KEY_PAUSE }, // (121) Break / Pause key.
|
||||
{ AKEYCODE_MOVE_HOME, KEY_HOME }, // (122) Home Movement key.
|
||||
{ AKEYCODE_MOVE_END, KEY_END }, // (123) End Movement key.
|
||||
{ AKEYCODE_INSERT, KEY_INSERT }, // (124) Insert key.
|
||||
{ AKEYCODE_FORWARD, KEY_FORWARD }, // (125) Forward key.
|
||||
{ AKEYCODE_MEDIA_PLAY, KEY_MEDIAPLAY }, // (126) Play media key.
|
||||
|
|
|
@ -392,7 +392,7 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
|
|||
for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
|
||||
plugin.onRegisterPluginWithGodotNative();
|
||||
}
|
||||
setKeepScreenOn("True".equals(GodotLib.getGlobal("display/window/energy_saving/keep_screen_on")));
|
||||
setKeepScreenOn(Boolean.parseBoolean(GodotLib.getGlobal("display/window/energy_saving/keep_screen_on")));
|
||||
|
||||
// The Godot Android plugins are setup on completion of GodotLib.setup
|
||||
mainThreadHandler.post(() -> {
|
||||
|
|
|
@ -947,7 +947,7 @@ Error EditorExportPlatformIOS::_export_loading_screen_file(const Ref<EditorExpor
|
|||
Ref<Image> image;
|
||||
String image_path = p_dest_dir.plus_file("splash@2x.png");
|
||||
image.instance();
|
||||
Error err = image->load(custom_launch_image_2x);
|
||||
Error err = ImageLoader::load_image(custom_launch_image_2x, image);
|
||||
|
||||
if (err) {
|
||||
image.unref();
|
||||
|
@ -961,7 +961,7 @@ Error EditorExportPlatformIOS::_export_loading_screen_file(const Ref<EditorExpor
|
|||
image.unref();
|
||||
image_path = p_dest_dir.plus_file("splash@3x.png");
|
||||
image.instance();
|
||||
err = image->load(custom_launch_image_3x);
|
||||
err = ImageLoader::load_image(custom_launch_image_3x, image);
|
||||
|
||||
if (err) {
|
||||
image.unref();
|
||||
|
@ -978,7 +978,7 @@ Error EditorExportPlatformIOS::_export_loading_screen_file(const Ref<EditorExpor
|
|||
|
||||
if (!splash_path.empty()) {
|
||||
splash.instance();
|
||||
const Error err = splash->load(splash_path);
|
||||
const Error err = ImageLoader::load_image(splash_path, splash);
|
||||
if (err) {
|
||||
splash.unref();
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include "export.h"
|
||||
#include "codesign.h"
|
||||
|
||||
#include "core/io/image_loader.h"
|
||||
#include "core/io/marshalls.h"
|
||||
#include "core/io/resource_saver.h"
|
||||
#include "core/io/zip_io.h"
|
||||
|
@ -813,7 +814,7 @@ Error EditorExportPlatformOSX::_code_sign(const Ref<EditorExportPreset> &p_prese
|
|||
String str;
|
||||
int exitcode = 0;
|
||||
|
||||
Error err = OS::get_singleton()->execute("codesign", args, true, NULL, &str, NULL, true);
|
||||
Error err = OS::get_singleton()->execute("codesign", args, true, NULL, &str, &exitcode, true);
|
||||
if (err != OK) {
|
||||
add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), TTR("Could not start codesign executable, make sure Xcode command line tools are installed."));
|
||||
return err;
|
||||
|
@ -1180,8 +1181,8 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
|
|||
} else {
|
||||
Ref<Image> icon;
|
||||
icon.instance();
|
||||
icon->load(iconpath);
|
||||
if (!icon->empty()) {
|
||||
err = ImageLoader::load_image(iconpath, icon);
|
||||
if (err == OK && !icon->empty()) {
|
||||
_make_icon(p_preset, icon, data);
|
||||
}
|
||||
}
|
||||
|
@ -1719,8 +1720,8 @@ bool EditorExportPlatformOSX::has_valid_project_configuration(const Ref<EditorEx
|
|||
if (p_preset->get("notarization/apple_id_name") != "") {
|
||||
if (p_preset->get("notarization/apple_id_password") == "") {
|
||||
err += TTR("Notarization: Apple ID password not specified.") + "\n";
|
||||
valid = false;
|
||||
}
|
||||
valid = false;
|
||||
}
|
||||
if (p_preset->get("notarization/api_uuid") != "") {
|
||||
if (p_preset->get("notarization/api_key") == "") {
|
||||
|
|
|
@ -137,7 +137,7 @@ static NSCursor *cursorFromSelector(SEL selector, SEL fallback = nil) {
|
|||
// special case handling of command-period, which is traditionally a special
|
||||
// shortcut in macOS and doesn't arrive at our regular keyDown handler.
|
||||
if ([event type] == NSEventTypeKeyDown) {
|
||||
if (([event modifierFlags] & NSEventModifierFlagCommand) && [event keyCode] == 0x2f) {
|
||||
if ((([event modifierFlags] & NSEventModifierFlagDeviceIndependentFlagsMask) == NSEventModifierFlagCommand) && [event keyCode] == 0x2f) {
|
||||
Ref<InputEventKey> k;
|
||||
k.instance();
|
||||
|
||||
|
@ -195,7 +195,9 @@ static NSCursor *cursorFromSelector(SEL selector, SEL fallback = nil) {
|
|||
|
||||
- (void)applicationDidFinishLaunching:(NSNotification *)notice {
|
||||
NSString *nsappname = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleName"];
|
||||
if (nsappname == nil || isatty(STDOUT_FILENO) || isatty(STDIN_FILENO) || isatty(STDERR_FILENO)) {
|
||||
NSString *nsbundleid_env = [NSString stringWithUTF8String:getenv("__CFBundleIdentifier")];
|
||||
NSString *nsbundleid = [[NSBundle mainBundle] bundleIdentifier];
|
||||
if (nsappname == nil || isatty(STDOUT_FILENO) || isatty(STDIN_FILENO) || isatty(STDERR_FILENO) || ![nsbundleid isEqualToString:nsbundleid_env]) {
|
||||
// If the executable is started from terminal or is not bundled, macOS WindowServer won't register and activate app window correctly (menu and title bar are grayed out and input ignored).
|
||||
[self performSelector:@selector(forceUnbundledWindowActivationHackStep1) withObject:nil afterDelay:0.02];
|
||||
}
|
||||
|
|
|
@ -4003,11 +4003,8 @@ OS_Windows::OS_Windows(HINSTANCE _hInstance) {
|
|||
//
|
||||
// NOTE: The engine does not use ANSI escape codes to color error/warning messages; it uses Windows API calls instead.
|
||||
// Therefore, error/warning messages are still colored on Windows versions older than 10.
|
||||
HANDLE stdoutHandle;
|
||||
stdoutHandle = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
DWORD outMode = 0;
|
||||
outMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
|
||||
|
||||
HANDLE stdoutHandle = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
DWORD outMode = ENABLE_PROCESSED_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING;
|
||||
if (!SetConsoleMode(stdoutHandle, outMode)) {
|
||||
// Windows 8.1 or below, or Windows 10 prior to Anniversary Update.
|
||||
print_verbose("Can't set the ENABLE_VIRTUAL_TERMINAL_PROCESSING Windows console mode.");
|
||||
|
|
|
@ -400,7 +400,10 @@ def configure(env):
|
|||
if platform.system() == "Linux":
|
||||
env.Append(LIBS=["dl"])
|
||||
|
||||
if platform.system().find("BSD") >= 0:
|
||||
if not env["execinfo"] and platform.libc_ver()[0] != "glibc":
|
||||
# The default crash handler depends on glibc, so if the host uses
|
||||
# a different libc (BSD libc, musl), fall back to libexecinfo.
|
||||
print("Note: Using `execinfo=yes` for the crash handler as required on platforms where glibc is missing.")
|
||||
env["execinfo"] = True
|
||||
|
||||
if env["execinfo"]:
|
||||
|
|
|
@ -203,7 +203,10 @@ int detect_prime() {
|
|||
print_verbose("Couldn't write vendor/renderer string.");
|
||||
}
|
||||
close(fdset[1]);
|
||||
exit(0);
|
||||
|
||||
// The function quick_exit() is used because exit() will call destructors on static objects copied by fork().
|
||||
// These objects will be freed anyway when the process finishes execution.
|
||||
quick_exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -707,10 +707,14 @@ void _pb_start_simulation(const Skeleton *p_skeleton, Node *p_node, const Vector
|
|||
PhysicalBone *pb = Object::cast_to<PhysicalBone>(p_node);
|
||||
if (pb) {
|
||||
bool sim = false;
|
||||
for (int i = p_sim_bones.size() - 1; 0 <= i; --i) {
|
||||
if (p_sim_bones[i] == pb->get_bone_id() || p_skeleton->is_bone_parent_of(pb->get_bone_id(), p_sim_bones[i])) {
|
||||
sim = true;
|
||||
break;
|
||||
if (p_sim_bones.empty()) { // If no bones is specified, activate ragdoll on full body.
|
||||
sim = true;
|
||||
} else {
|
||||
for (int i = p_sim_bones.size() - 1; 0 <= i; --i) {
|
||||
if (p_sim_bones[i] == pb->get_bone_id() || p_skeleton->is_bone_parent_of(pb->get_bone_id(), p_sim_bones[i])) {
|
||||
sim = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -725,9 +729,7 @@ void _pb_start_simulation(const Skeleton *p_skeleton, Node *p_node, const Vector
|
|||
|
||||
void Skeleton::physical_bones_start_simulation_on(const Array &p_bones) {
|
||||
Vector<int> sim_bones;
|
||||
if (p_bones.size() <= 0) {
|
||||
sim_bones.push_back(0); // if no bones is specified, activate ragdoll on full body
|
||||
} else {
|
||||
if (p_bones.size() > 0) {
|
||||
sim_bones.resize(p_bones.size());
|
||||
int c = 0;
|
||||
for (int i = sim_bones.size() - 1; 0 <= i; --i) {
|
||||
|
|
|
@ -195,12 +195,15 @@ void ColorPicker::_html_entered(const String &p_html) {
|
|||
return;
|
||||
}
|
||||
|
||||
float last_alpha = color.a;
|
||||
Color previous_color = color;
|
||||
color = Color::html(p_html);
|
||||
if (!is_editing_alpha()) {
|
||||
color.a = last_alpha;
|
||||
color.a = previous_color.a;
|
||||
}
|
||||
|
||||
if (color == previous_color) {
|
||||
return;
|
||||
}
|
||||
if (!is_inside_tree()) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -2887,10 +2887,14 @@ void Node::print_stray_nodes() {
|
|||
}
|
||||
|
||||
void Node::queue_delete() {
|
||||
// There are users which instantiate multiple scene trees for their games.
|
||||
// Use the node's own tree to handle its deletion when relevant.
|
||||
if (is_inside_tree()) {
|
||||
get_tree()->queue_delete(this);
|
||||
} else {
|
||||
SceneTree::get_singleton()->queue_delete(this);
|
||||
SceneTree *tree = SceneTree::get_singleton();
|
||||
ERR_FAIL_NULL_MSG(tree, "Can't queue free a node when no SceneTree is available.");
|
||||
tree->queue_delete(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -167,8 +167,10 @@ static bool _collect_inheritance_chain(const Ref<SceneState> &p_state, const Nod
|
|||
state = state->get_base_scene_state();
|
||||
}
|
||||
|
||||
for (int i = inheritance_states.size() - 1; i >= 0; --i) {
|
||||
r_states_stack.push_back(inheritance_states[i]);
|
||||
if (inheritance_states.size() > 0) {
|
||||
for (int i = inheritance_states.size() - 1; i >= 0; --i) {
|
||||
r_states_stack.push_back(inheritance_states[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
|
@ -212,10 +214,12 @@ Vector<SceneState::PackState> PropertyUtils::get_node_states_stack(const Node *p
|
|||
{
|
||||
states_stack_ret.resize(states_stack.size());
|
||||
_FastPackState *ps = states_stack.ptr();
|
||||
for (int i = states_stack.size() - 1; i >= 0; --i) {
|
||||
states_stack_ret.write[i].state.reference_ptr(ps->state);
|
||||
states_stack_ret.write[i].node = ps->node;
|
||||
++ps;
|
||||
if (states_stack.size() > 0) {
|
||||
for (int i = states_stack.size() - 1; i >= 0; --i) {
|
||||
states_stack_ret.write[i].state.reference_ptr(ps->state);
|
||||
states_stack_ret.write[i].node = ps->node;
|
||||
++ps;
|
||||
}
|
||||
}
|
||||
}
|
||||
return states_stack_ret;
|
||||
|
|
|
@ -774,6 +774,7 @@ void CylinderMesh::create_mesh_array(Array &p_arr, float top_radius, float botto
|
|||
|
||||
thisrow = 0;
|
||||
prevrow = 0;
|
||||
const real_t side_normal_y = (bottom_radius - top_radius) / height;
|
||||
for (j = 0; j <= (rings + 1); j++) {
|
||||
v = j;
|
||||
v /= (rings + 1);
|
||||
|
@ -792,7 +793,7 @@ void CylinderMesh::create_mesh_array(Array &p_arr, float top_radius, float botto
|
|||
|
||||
Vector3 p = Vector3(x * radius, y, z * radius);
|
||||
points.push_back(p);
|
||||
normals.push_back(Vector3(x, 0.0, z));
|
||||
normals.push_back(Vector3(x, side_normal_y, z).normalized());
|
||||
ADD_TANGENT(z, 0.0, -x, 1.0)
|
||||
uvs.push_back(Vector2(u, v * 0.5));
|
||||
point++;
|
||||
|
|
|
@ -494,7 +494,7 @@ Files extracted from upstream source:
|
|||
## recastnavigation
|
||||
|
||||
- Upstream: https://github.com/recastnavigation/recastnavigation
|
||||
- Version: git (5a870d427e47abd4a8e4ce58a95582ec049434d5, 2022)
|
||||
- Version: git (4fef0446609b23d6ac180ed822817571525528a1, 2022)
|
||||
- License: zlib
|
||||
|
||||
Files extracted from upstream source:
|
||||
|
|
|
@ -22,13 +22,16 @@
|
|||
/// The value of PI used by Recast.
|
||||
static const float RC_PI = 3.14159265f;
|
||||
|
||||
/// Used to ignore unused function parameters and silence any compiler warnings.
|
||||
template<class T> void rcIgnoreUnused(const T&) { }
|
||||
|
||||
/// Recast log categories.
|
||||
/// @see rcContext
|
||||
enum rcLogCategory
|
||||
{
|
||||
RC_LOG_PROGRESS = 1, ///< A progress log entry.
|
||||
RC_LOG_WARNING, ///< A warning log entry.
|
||||
RC_LOG_ERROR, ///< An error log entry.
|
||||
RC_LOG_ERROR ///< An error log entry.
|
||||
};
|
||||
|
||||
/// Recast performance timer categories.
|
||||
|
@ -101,7 +104,6 @@ enum rcTimerLabel
|
|||
class rcContext
|
||||
{
|
||||
public:
|
||||
|
||||
/// Contructor.
|
||||
/// @param[in] state TRUE if the logging and performance timers should be enabled. [Default: true]
|
||||
inline rcContext(bool state = true) : m_logEnabled(state), m_timerEnabled(state) {}
|
||||
|
@ -140,31 +142,30 @@ public:
|
|||
inline int getAccumulatedTime(const rcTimerLabel label) const { return m_timerEnabled ? doGetAccumulatedTime(label) : -1; }
|
||||
|
||||
protected:
|
||||
|
||||
/// Clears all log entries.
|
||||
virtual void doResetLog() {}
|
||||
virtual void doResetLog();
|
||||
|
||||
/// Logs a message.
|
||||
/// @param[in] category The category of the message.
|
||||
/// @param[in] msg The formatted message.
|
||||
/// @param[in] len The length of the formatted message.
|
||||
virtual void doLog(const rcLogCategory /*category*/, const char* /*msg*/, const int /*len*/) {}
|
||||
virtual void doLog(const rcLogCategory category, const char* msg, const int len) { rcIgnoreUnused(category); rcIgnoreUnused(msg); rcIgnoreUnused(len); }
|
||||
|
||||
/// Clears all timers. (Resets all to unused.)
|
||||
virtual void doResetTimers() {}
|
||||
|
||||
/// Starts the specified performance timer.
|
||||
/// @param[in] label The category of timer.
|
||||
virtual void doStartTimer(const rcTimerLabel /*label*/) {}
|
||||
virtual void doStartTimer(const rcTimerLabel label) { rcIgnoreUnused(label); }
|
||||
|
||||
/// Stops the specified performance timer.
|
||||
/// @param[in] label The category of the timer.
|
||||
virtual void doStopTimer(const rcTimerLabel /*label*/) {}
|
||||
virtual void doStopTimer(const rcTimerLabel label) { rcIgnoreUnused(label); }
|
||||
|
||||
/// Returns the total accumulated time of the specified performance timer.
|
||||
/// @param[in] label The category of the timer.
|
||||
/// @return The accumulated time of the timer, or -1 if timers are disabled or the timer has never been started.
|
||||
virtual int doGetAccumulatedTime(const rcTimerLabel /*label*/) const { return -1; }
|
||||
virtual int doGetAccumulatedTime(const rcTimerLabel label) const { rcIgnoreUnused(label); return -1; }
|
||||
|
||||
/// True if logging is enabled.
|
||||
bool m_logEnabled;
|
||||
|
@ -564,7 +565,7 @@ static const int RC_AREA_BORDER = 0x20000;
|
|||
enum rcBuildContoursFlags
|
||||
{
|
||||
RC_CONTOUR_TESS_WALL_EDGES = 0x01, ///< Tessellate solid (impassable) edges during contour simplification.
|
||||
RC_CONTOUR_TESS_AREA_EDGES = 0x02, ///< Tessellate edges between areas during contour simplification.
|
||||
RC_CONTOUR_TESS_AREA_EDGES = 0x02 ///< Tessellate edges between areas during contour simplification.
|
||||
};
|
||||
|
||||
/// Applied to the region id field of contour vertices in order to extract the region id.
|
||||
|
@ -595,11 +596,6 @@ static const int RC_NOT_CONNECTED = 0x3f;
|
|||
/// @name General helper functions
|
||||
/// @{
|
||||
|
||||
/// Used to ignore a function parameter. VS complains about unused parameters
|
||||
/// and this silences the warning.
|
||||
/// @param [in] _ Unused parameter
|
||||
template<class T> void rcIgnoreUnused(const T&) { }
|
||||
|
||||
/// Swaps the values of the two parameters.
|
||||
/// @param[in,out] a Value A
|
||||
/// @param[in,out] b Value B
|
||||
|
@ -996,6 +992,7 @@ void rcMarkConvexPolyArea(rcContext* ctx, const float* verts, const int nverts,
|
|||
/// @ingroup recast
|
||||
/// @param[in] verts The vertices of the polygon [Form: (x, y, z) * @p nverts]
|
||||
/// @param[in] nverts The number of vertices in the polygon.
|
||||
/// @param[in] offset How much to offset the polygon by. [Units: wu]
|
||||
/// @param[out] outVerts The offset vertices (should hold up to 2 * @p nverts) [Form: (x, y, z) * return value]
|
||||
/// @param[in] maxOutVerts The max number of vertices that can be stored to @p outVerts.
|
||||
/// @returns Number of vertices in the offset polygon or 0 if too few vertices in @p outVerts.
|
||||
|
|
|
@ -112,7 +112,7 @@ class rcVectorBase {
|
|||
typedef rcSizeType size_type;
|
||||
typedef T value_type;
|
||||
|
||||
rcVectorBase() : m_size(0), m_cap(0), m_data(0) {};
|
||||
rcVectorBase() : m_size(0), m_cap(0), m_data(0) {}
|
||||
rcVectorBase(const rcVectorBase<T, H>& other) : m_size(0), m_cap(0), m_data(0) { assign(other.begin(), other.end()); }
|
||||
explicit rcVectorBase(rcSizeType count) : m_size(0), m_cap(0), m_data(0) { resize(count); }
|
||||
rcVectorBase(rcSizeType count, const T& value) : m_size(0), m_cap(0), m_data(0) { resize(count, value); }
|
||||
|
@ -142,8 +142,8 @@ class rcVectorBase {
|
|||
|
||||
const T& front() const { rcAssert(m_size); return m_data[0]; }
|
||||
T& front() { rcAssert(m_size); return m_data[0]; }
|
||||
const T& back() const { rcAssert(m_size); return m_data[m_size - 1]; };
|
||||
T& back() { rcAssert(m_size); return m_data[m_size - 1]; };
|
||||
const T& back() const { rcAssert(m_size); return m_data[m_size - 1]; }
|
||||
T& back() { rcAssert(m_size); return m_data[m_size - 1]; }
|
||||
const T* data() const { return m_data; }
|
||||
T* data() { return m_data; }
|
||||
|
||||
|
|
|
@ -94,6 +94,11 @@ void rcContext::log(const rcLogCategory category, const char* format, ...)
|
|||
doLog(category, msg, len);
|
||||
}
|
||||
|
||||
void rcContext::doResetLog()
|
||||
{
|
||||
// Defined out of line to fix the weak v-tables warning
|
||||
}
|
||||
|
||||
rcHeightfield* rcAllocHeightfield()
|
||||
{
|
||||
return rcNew<rcHeightfield>(RC_ALLOC_PERM);
|
||||
|
|
|
@ -566,7 +566,6 @@ static bool canRemoveVertex(rcContext* ctx, rcPolyMesh& mesh, const unsigned sho
|
|||
const int nvp = mesh.nvp;
|
||||
|
||||
// Count number of polygons to remove.
|
||||
int numRemovedVerts = 0;
|
||||
int numTouchedVerts = 0;
|
||||
int numRemainingEdges = 0;
|
||||
for (int i = 0; i < mesh.npolys; ++i)
|
||||
|
@ -586,7 +585,6 @@ static bool canRemoveVertex(rcContext* ctx, rcPolyMesh& mesh, const unsigned sho
|
|||
}
|
||||
if (numRemoved)
|
||||
{
|
||||
numRemovedVerts += numRemoved;
|
||||
numRemainingEdges += numVerts-(numRemoved+1);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -284,7 +284,7 @@ static unsigned short getHeight(const float fx, const float fy, const float fz,
|
|||
enum EdgeValues
|
||||
{
|
||||
EV_UNDEF = -1,
|
||||
EV_HULL = -2,
|
||||
EV_HULL = -2
|
||||
};
|
||||
|
||||
static int findEdge(const int* edges, int nedges, int s, int t)
|
||||
|
|
|
@ -264,7 +264,8 @@ static bool rasterizeTri(const float* v0, const float* v1, const float* v2,
|
|||
// Calculate the footprint of the triangle on the grid's y-axis
|
||||
int y0 = (int)((tmin[2] - bmin[2])*ics);
|
||||
int y1 = (int)((tmax[2] - bmin[2])*ics);
|
||||
y0 = rcClamp(y0, 0, h-1);
|
||||
// use -1 rather than 0 to cut the polygon properly at the start of the tile
|
||||
y0 = rcClamp(y0, -1, h-1);
|
||||
y1 = rcClamp(y1, 0, h-1);
|
||||
|
||||
// Clip the triangle into all grid cells it touches.
|
||||
|
@ -283,7 +284,7 @@ static bool rasterizeTri(const float* v0, const float* v1, const float* v2,
|
|||
dividePoly(in, nvIn, inrow, &nvrow, p1, &nvIn, cz+cs, 2);
|
||||
rcSwap(in, p1);
|
||||
if (nvrow < 3) continue;
|
||||
|
||||
if (y < 0) continue;
|
||||
// find the horizontal bounds in the row
|
||||
float minX = inrow[0], maxX = inrow[0];
|
||||
for (int i=1; i<nvrow; ++i)
|
||||
|
@ -293,7 +294,10 @@ static bool rasterizeTri(const float* v0, const float* v1, const float* v2,
|
|||
}
|
||||
int x0 = (int)((minX - bmin[0])*ics);
|
||||
int x1 = (int)((maxX - bmin[0])*ics);
|
||||
x0 = rcClamp(x0, 0, w-1);
|
||||
if (x1 < 0 || x0 >= w) {
|
||||
continue;
|
||||
}
|
||||
x0 = rcClamp(x0, -1, w-1);
|
||||
x1 = rcClamp(x1, 0, w-1);
|
||||
|
||||
int nv, nv2 = nvrow;
|
||||
|
@ -305,7 +309,7 @@ static bool rasterizeTri(const float* v0, const float* v1, const float* v2,
|
|||
dividePoly(inrow, nv2, p1, &nv, p2, &nv2, cx+cs, 0);
|
||||
rcSwap(inrow, p2);
|
||||
if (nv < 3) continue;
|
||||
|
||||
if (x < 0) continue;
|
||||
// Calculate min and max of the span.
|
||||
float smin = p1[1], smax = p1[1];
|
||||
for (int i = 1; i < nv; ++i)
|
||||
|
|
Loading…
Reference in New Issue