feat: updated engine version to 4.4-rc1

This commit is contained in:
Sara 2025-02-23 14:38:14 +01:00
parent ee00efde1f
commit 21ba8e33af
5459 changed files with 1128836 additions and 198305 deletions

View file

@ -1,4 +1,5 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
Import("env")

View file

@ -60,6 +60,9 @@ void CapsuleShape2D::_update_shape() {
void CapsuleShape2D::set_radius(real_t p_radius) {
ERR_FAIL_COND_MSG(p_radius < 0, "CapsuleShape2D radius cannot be negative.");
if (radius == p_radius) {
return;
}
radius = p_radius;
if (radius > height * 0.5) {
height = radius * 2.0;
@ -73,6 +76,9 @@ real_t CapsuleShape2D::get_radius() const {
void CapsuleShape2D::set_height(real_t p_height) {
ERR_FAIL_COND_MSG(p_height < 0, "CapsuleShape2D height cannot be negative.");
if (height == p_height) {
return;
}
height = p_height;
if (radius > height * 0.5) {
radius = height * 0.5;

View file

@ -44,6 +44,9 @@ void CircleShape2D::_update_shape() {
void CircleShape2D::set_radius(real_t p_radius) {
ERR_FAIL_COND_MSG(p_radius < 0, "CircleShape2D radius cannot be negative.");
if (radius == p_radius) {
return;
}
radius = p_radius;
_update_shape();
}

View file

@ -30,33 +30,37 @@
#include "navigation_mesh_source_geometry_data_2d.h"
#include "scene/resources/mesh.h"
#include "core/variant/typed_array.h"
void NavigationMeshSourceGeometryData2D::clear() {
RWLockWrite write_lock(geometry_rwlock);
traversable_outlines.clear();
obstruction_outlines.clear();
_projected_obstructions.clear();
bounds_dirty = true;
}
bool NavigationMeshSourceGeometryData2D::has_data() {
RWLockRead read_lock(geometry_rwlock);
return traversable_outlines.size();
};
}
void NavigationMeshSourceGeometryData2D::clear_projected_obstructions() {
RWLockWrite write_lock(geometry_rwlock);
_projected_obstructions.clear();
bounds_dirty = true;
}
void NavigationMeshSourceGeometryData2D::_set_traversable_outlines(const Vector<Vector<Vector2>> &p_traversable_outlines) {
RWLockWrite write_lock(geometry_rwlock);
traversable_outlines = p_traversable_outlines;
bounds_dirty = true;
}
void NavigationMeshSourceGeometryData2D::_set_obstruction_outlines(const Vector<Vector<Vector2>> &p_obstruction_outlines) {
RWLockWrite write_lock(geometry_rwlock);
obstruction_outlines = p_obstruction_outlines;
bounds_dirty = true;
}
const Vector<Vector<Vector2>> &NavigationMeshSourceGeometryData2D::_get_traversable_outlines() const {
@ -73,6 +77,7 @@ void NavigationMeshSourceGeometryData2D::_add_traversable_outline(const Vector<V
if (p_shape_outline.size() > 1) {
RWLockWrite write_lock(geometry_rwlock);
traversable_outlines.push_back(p_shape_outline);
bounds_dirty = true;
}
}
@ -80,6 +85,7 @@ void NavigationMeshSourceGeometryData2D::_add_obstruction_outline(const Vector<V
if (p_shape_outline.size() > 1) {
RWLockWrite write_lock(geometry_rwlock);
obstruction_outlines.push_back(p_shape_outline);
bounds_dirty = true;
}
}
@ -89,6 +95,7 @@ void NavigationMeshSourceGeometryData2D::set_traversable_outlines(const TypedArr
for (int i = 0; i < p_traversable_outlines.size(); i++) {
traversable_outlines.write[i] = p_traversable_outlines[i];
}
bounds_dirty = true;
}
TypedArray<Vector<Vector2>> NavigationMeshSourceGeometryData2D::get_traversable_outlines() const {
@ -108,6 +115,7 @@ void NavigationMeshSourceGeometryData2D::set_obstruction_outlines(const TypedArr
for (int i = 0; i < p_obstruction_outlines.size(); i++) {
obstruction_outlines.write[i] = p_obstruction_outlines[i];
}
bounds_dirty = true;
}
TypedArray<Vector<Vector2>> NavigationMeshSourceGeometryData2D::get_obstruction_outlines() const {
@ -128,6 +136,7 @@ void NavigationMeshSourceGeometryData2D::append_traversable_outlines(const Typed
for (int i = traversable_outlines_size; i < p_traversable_outlines.size(); i++) {
traversable_outlines.write[i] = p_traversable_outlines[i];
}
bounds_dirty = true;
}
void NavigationMeshSourceGeometryData2D::append_obstruction_outlines(const TypedArray<Vector<Vector2>> &p_obstruction_outlines) {
@ -137,6 +146,7 @@ void NavigationMeshSourceGeometryData2D::append_obstruction_outlines(const Typed
for (int i = obstruction_outlines_size; i < p_obstruction_outlines.size(); i++) {
obstruction_outlines.write[i] = p_obstruction_outlines[i];
}
bounds_dirty = true;
}
void NavigationMeshSourceGeometryData2D::add_traversable_outline(const PackedVector2Array &p_shape_outline) {
@ -148,6 +158,7 @@ void NavigationMeshSourceGeometryData2D::add_traversable_outline(const PackedVec
traversable_outline.write[i] = p_shape_outline[i];
}
traversable_outlines.push_back(traversable_outline);
bounds_dirty = true;
}
}
@ -160,11 +171,12 @@ void NavigationMeshSourceGeometryData2D::add_obstruction_outline(const PackedVec
obstruction_outline.write[i] = p_shape_outline[i];
}
obstruction_outlines.push_back(obstruction_outline);
bounds_dirty = true;
}
}
void NavigationMeshSourceGeometryData2D::merge(const Ref<NavigationMeshSourceGeometryData2D> &p_other_geometry) {
ERR_FAIL_NULL(p_other_geometry);
ERR_FAIL_COND(p_other_geometry.is_null());
Vector<Vector<Vector2>> other_traversable_outlines;
Vector<Vector<Vector2>> other_obstruction_outlines;
@ -176,6 +188,7 @@ void NavigationMeshSourceGeometryData2D::merge(const Ref<NavigationMeshSourceGeo
traversable_outlines.append_array(other_traversable_outlines);
obstruction_outlines.append_array(other_obstruction_outlines);
_projected_obstructions.append_array(other_projected_obstructions);
bounds_dirty = true;
}
void NavigationMeshSourceGeometryData2D::add_projected_obstruction(const Vector<Vector2> &p_vertices, bool p_carve) {
@ -195,6 +208,7 @@ void NavigationMeshSourceGeometryData2D::add_projected_obstruction(const Vector<
RWLockWrite write_lock(geometry_rwlock);
_projected_obstructions.push_back(projected_obstruction);
bounds_dirty = true;
}
void NavigationMeshSourceGeometryData2D::set_projected_obstructions(const Array &p_array) {
@ -217,6 +231,7 @@ void NavigationMeshSourceGeometryData2D::set_projected_obstructions(const Array
RWLockWrite write_lock(geometry_rwlock);
_projected_obstructions.push_back(projected_obstruction);
bounds_dirty = true;
}
}
@ -266,6 +281,7 @@ void NavigationMeshSourceGeometryData2D::set_data(const Vector<Vector<Vector2>>
traversable_outlines = p_traversable_outlines;
obstruction_outlines = p_obstruction_outlines;
_projected_obstructions = p_projected_obstructions;
bounds_dirty = true;
}
void NavigationMeshSourceGeometryData2D::get_data(Vector<Vector<Vector2>> &r_traversable_outlines, Vector<Vector<Vector2>> &r_obstruction_outlines, Vector<ProjectedObstruction> &r_projected_obstructions) {
@ -275,6 +291,58 @@ void NavigationMeshSourceGeometryData2D::get_data(Vector<Vector<Vector2>> &r_tra
r_projected_obstructions = _projected_obstructions;
}
Rect2 NavigationMeshSourceGeometryData2D::get_bounds() {
geometry_rwlock.read_lock();
if (bounds_dirty) {
geometry_rwlock.read_unlock();
RWLockWrite write_lock(geometry_rwlock);
bounds_dirty = false;
bounds = Rect2();
bool first_vertex = true;
for (const Vector<Vector2> &traversable_outline : traversable_outlines) {
for (const Vector2 &traversable_point : traversable_outline) {
if (first_vertex) {
first_vertex = false;
bounds.position = traversable_point;
} else {
bounds.expand_to(traversable_point);
}
}
}
for (const Vector<Vector2> &obstruction_outline : obstruction_outlines) {
for (const Vector2 &obstruction_point : obstruction_outline) {
if (first_vertex) {
first_vertex = false;
bounds.position = obstruction_point;
} else {
bounds.expand_to(obstruction_point);
}
}
}
for (const ProjectedObstruction &projected_obstruction : _projected_obstructions) {
for (int i = 0; i < projected_obstruction.vertices.size() / 2; i++) {
const Vector2 vertex = Vector2(projected_obstruction.vertices[i * 2], projected_obstruction.vertices[i * 2 + 1]);
if (first_vertex) {
first_vertex = false;
bounds.position = vertex;
} else {
bounds.expand_to(vertex);
}
}
}
} else {
geometry_rwlock.read_unlock();
}
RWLockRead read_lock(geometry_rwlock);
return bounds;
}
void NavigationMeshSourceGeometryData2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("clear"), &NavigationMeshSourceGeometryData2D::clear);
ClassDB::bind_method(D_METHOD("has_data"), &NavigationMeshSourceGeometryData2D::has_data);
@ -298,6 +366,8 @@ void NavigationMeshSourceGeometryData2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_projected_obstructions", "projected_obstructions"), &NavigationMeshSourceGeometryData2D::set_projected_obstructions);
ClassDB::bind_method(D_METHOD("get_projected_obstructions"), &NavigationMeshSourceGeometryData2D::get_projected_obstructions);
ClassDB::bind_method(D_METHOD("get_bounds"), &NavigationMeshSourceGeometryData2D::get_bounds);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "traversable_outlines", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_traversable_outlines", "get_traversable_outlines");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "obstruction_outlines", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_obstruction_outlines", "get_obstruction_outlines");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "projected_obstructions", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_projected_obstructions", "get_projected_obstructions");

View file

@ -31,17 +31,21 @@
#ifndef NAVIGATION_MESH_SOURCE_GEOMETRY_DATA_2D_H
#define NAVIGATION_MESH_SOURCE_GEOMETRY_DATA_2D_H
#include "core/io/resource.h"
#include "core/os/rw_lock.h"
#include "scene/2d/node_2d.h"
#include "scene/resources/2d/navigation_polygon.h"
class NavigationMeshSourceGeometryData2D : public Resource {
friend class NavMeshGenerator2D;
GDCLASS(NavigationMeshSourceGeometryData2D, Resource);
RWLock geometry_rwlock;
Vector<Vector<Vector2>> traversable_outlines;
Vector<Vector<Vector2>> obstruction_outlines;
Rect2 bounds;
bool bounds_dirty = true;
public:
struct ProjectedObstruction;
@ -103,6 +107,8 @@ public:
void set_data(const Vector<Vector<Vector2>> &p_traversable_outlines, const Vector<Vector<Vector2>> &p_obstruction_outlines, Vector<ProjectedObstruction> &p_projected_obstructions);
void get_data(Vector<Vector<Vector2>> &r_traversable_outlines, Vector<Vector<Vector2>> &r_obstruction_outlines, Vector<ProjectedObstruction> &r_projected_obstructions);
Rect2 get_bounds();
NavigationMeshSourceGeometryData2D() {}
~NavigationMeshSourceGeometryData2D() { clear(); }
};

View file

@ -32,11 +32,10 @@
#include "core/math/geometry_2d.h"
#include "core/os/mutex.h"
#include "servers/navigation_server_2d.h"
#include "thirdparty/misc/polypartition.h"
#ifdef TOOLS_ENABLED
#ifdef DEBUG_ENABLED
Rect2 NavigationPolygon::_edit_get_rect() const {
RWLockRead read_lock(rwlock);
if (rect_cache_dirty) {
@ -79,7 +78,7 @@ bool NavigationPolygon::_edit_is_selected_on_click(const Point2 &p_point, double
}
return false;
}
#endif
#endif // DEBUG_ENABLED
void NavigationPolygon::set_vertices(const Vector<Vector2> &p_vertices) {
RWLockWrite write_lock(rwlock);
@ -104,7 +103,7 @@ void NavigationPolygon::_set_polygons(const TypedArray<Vector<int32_t>> &p_array
}
polygons.resize(p_array.size());
for (int i = 0; i < p_array.size(); i++) {
polygons.write[i].indices = p_array[i];
polygons.write[i] = p_array[i];
}
}
@ -113,7 +112,7 @@ TypedArray<Vector<int32_t>> NavigationPolygon::_get_polygons() const {
TypedArray<Vector<int32_t>> ret;
ret.resize(polygons.size());
for (int i = 0; i < ret.size(); i++) {
ret[i] = polygons[i].indices;
ret[i] = polygons[i];
}
return ret;
@ -141,9 +140,7 @@ TypedArray<Vector<Vector2>> NavigationPolygon::_get_outlines() const {
void NavigationPolygon::add_polygon(const Vector<int> &p_polygon) {
RWLockWrite write_lock(rwlock);
Polygon polygon;
polygon.indices = p_polygon;
polygons.push_back(polygon);
polygons.push_back(p_polygon);
{
MutexLock lock(navigation_mesh_generation);
navigation_mesh.unref();
@ -164,7 +161,7 @@ int NavigationPolygon::get_polygon_count() const {
Vector<int> NavigationPolygon::get_polygon(int p_idx) {
RWLockRead read_lock(rwlock);
ERR_FAIL_INDEX_V(p_idx, polygons.size(), Vector<int>());
return polygons[p_idx].indices;
return polygons[p_idx];
}
void NavigationPolygon::clear_polygons() {
@ -189,10 +186,19 @@ void NavigationPolygon::clear() {
void NavigationPolygon::set_data(const Vector<Vector2> &p_vertices, const Vector<Vector<int>> &p_polygons) {
RWLockWrite write_lock(rwlock);
vertices = p_vertices;
polygons.resize(p_polygons.size());
for (int i = 0; i < p_polygons.size(); i++) {
polygons.write[i].indices = p_polygons[i];
polygons = p_polygons;
{
MutexLock lock(navigation_mesh_generation);
navigation_mesh.unref();
}
}
void NavigationPolygon::set_data(const Vector<Vector2> &p_vertices, const Vector<Vector<int>> &p_polygons, const Vector<Vector<Vector2>> &p_outlines) {
RWLockWrite write_lock(rwlock);
vertices = p_vertices;
polygons = p_polygons;
outlines = p_outlines;
rect_cache_dirty = true;
{
MutexLock lock(navigation_mesh_generation);
navigation_mesh.unref();
@ -202,10 +208,14 @@ void NavigationPolygon::set_data(const Vector<Vector2> &p_vertices, const Vector
void NavigationPolygon::get_data(Vector<Vector2> &r_vertices, Vector<Vector<int>> &r_polygons) {
RWLockRead read_lock(rwlock);
r_vertices = vertices;
r_polygons.resize(polygons.size());
for (int i = 0; i < polygons.size(); i++) {
r_polygons.write[i] = polygons[i].indices;
}
r_polygons = polygons;
}
void NavigationPolygon::get_data(Vector<Vector2> &r_vertices, Vector<Vector<int>> &r_polygons, Vector<Vector<Vector2>> &r_outlines) {
RWLockRead read_lock(rwlock);
r_vertices = vertices;
r_polygons = polygons;
r_outlines = outlines;
}
Ref<NavigationMesh> NavigationPolygon::get_navigation_mesh() {
@ -237,6 +247,31 @@ Ref<NavigationMesh> NavigationPolygon::get_navigation_mesh() {
return navigation_mesh;
}
void NavigationPolygon::set_outlines(const Vector<Vector<Vector2>> &p_outlines) {
RWLockWrite write_lock(rwlock);
outlines = p_outlines;
rect_cache_dirty = true;
}
Vector<Vector<Vector2>> NavigationPolygon::get_outlines() const {
RWLockRead read_lock(rwlock);
return outlines;
}
void NavigationPolygon::set_polygons(const Vector<Vector<int>> &p_polygons) {
RWLockWrite write_lock(rwlock);
polygons = p_polygons;
{
MutexLock lock(navigation_mesh_generation);
navigation_mesh.unref();
}
}
Vector<Vector<int>> NavigationPolygon::get_polygons() const {
RWLockRead read_lock(rwlock);
return polygons;
}
void NavigationPolygon::add_outline(const Vector<Vector2> &p_outline) {
RWLockWrite write_lock(rwlock);
outlines.push_back(p_outline);
@ -364,7 +399,7 @@ void NavigationPolygon::make_polygons_from_outlines() {
for (List<TPPLPoly>::Element *I = out_poly.front(); I; I = I->next()) {
TPPLPoly &tp = I->get();
struct Polygon p;
Vector<int> p;
for (int64_t i = 0; i < tp.GetNumPoints(); i++) {
HashMap<Vector2, int>::Iterator E = points.find(tp[i]);
@ -372,7 +407,7 @@ void NavigationPolygon::make_polygons_from_outlines() {
E = points.insert(tp[i], vertices.size());
vertices.push_back(tp[i]);
}
p.indices.push_back(E->value);
p.push_back(E->value);
}
polygons.push_back(p);
@ -400,6 +435,15 @@ real_t NavigationPolygon::get_border_size() const {
return border_size;
}
void NavigationPolygon::set_sample_partition_type(SamplePartitionType p_value) {
ERR_FAIL_INDEX(p_value, SAMPLE_PARTITION_MAX);
partition_type = p_value;
}
NavigationPolygon::SamplePartitionType NavigationPolygon::get_sample_partition_type() const {
return partition_type;
}
void NavigationPolygon::set_parsed_geometry_type(ParsedGeometryType p_geometry_type) {
ERR_FAIL_INDEX(p_geometry_type, PARSED_GEOMETRY_MAX);
parsed_geometry_type = p_geometry_type;
@ -514,6 +558,9 @@ void NavigationPolygon::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_border_size", "border_size"), &NavigationPolygon::set_border_size);
ClassDB::bind_method(D_METHOD("get_border_size"), &NavigationPolygon::get_border_size);
ClassDB::bind_method(D_METHOD("set_sample_partition_type", "sample_partition_type"), &NavigationPolygon::set_sample_partition_type);
ClassDB::bind_method(D_METHOD("get_sample_partition_type"), &NavigationPolygon::get_sample_partition_type);
ClassDB::bind_method(D_METHOD("set_parsed_geometry_type", "geometry_type"), &NavigationPolygon::set_parsed_geometry_type);
ClassDB::bind_method(D_METHOD("get_parsed_geometry_type"), &NavigationPolygon::get_parsed_geometry_type);
@ -543,6 +590,8 @@ void NavigationPolygon::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "polygons", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_polygons", "_get_polygons");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "outlines", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_outlines", "_get_outlines");
ADD_GROUP("Sampling", "sample_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "sample_partition_type", PROPERTY_HINT_ENUM, "Convex Partition,Triangulate"), "set_sample_partition_type", "get_sample_partition_type");
ADD_GROUP("Geometry", "");
ADD_PROPERTY(PropertyInfo(Variant::INT, "parsed_geometry_type", PROPERTY_HINT_ENUM, "Mesh Instances,Static Colliders,Meshes and Static Colliders"), "set_parsed_geometry_type", "get_parsed_geometry_type");
ADD_PROPERTY(PropertyInfo(Variant::INT, "parsed_collision_mask", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_parsed_collision_mask", "get_parsed_collision_mask");
@ -559,6 +608,10 @@ void NavigationPolygon::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::RECT2, "baking_rect"), "set_baking_rect", "get_baking_rect");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "baking_rect_offset"), "set_baking_rect_offset", "get_baking_rect_offset");
BIND_ENUM_CONSTANT(SAMPLE_PARTITION_CONVEX_PARTITION);
BIND_ENUM_CONSTANT(SAMPLE_PARTITION_TRIANGULATE);
BIND_ENUM_CONSTANT(SAMPLE_PARTITION_MAX);
BIND_ENUM_CONSTANT(PARSED_GEOMETRY_MESH_INSTANCES);
BIND_ENUM_CONSTANT(PARSED_GEOMETRY_STATIC_COLLIDERS);
BIND_ENUM_CONSTANT(PARSED_GEOMETRY_BOTH);

View file

@ -33,16 +33,14 @@
#include "scene/2d/node_2d.h"
#include "scene/resources/navigation_mesh.h"
#include "servers/navigation/navigation_globals.h"
class NavigationPolygon : public Resource {
GDCLASS(NavigationPolygon, Resource);
RWLock rwlock;
Vector<Vector2> vertices;
struct Polygon {
Vector<int> indices;
};
Vector<Polygon> polygons;
Vector<Vector<int>> polygons;
Vector<Vector<Vector2>> outlines;
Vector<Vector<Vector2>> baked_outlines;
@ -53,7 +51,7 @@ class NavigationPolygon : public Resource {
// Navigation mesh
Ref<NavigationMesh> navigation_mesh;
real_t cell_size = 1.0f; // Must match ProjectSettings default 2D cell_size.
real_t cell_size = NavigationDefaults2D::navmesh_cell_size;
real_t border_size = 0.0f;
Rect2 baking_rect;
@ -70,10 +68,16 @@ protected:
TypedArray<Vector<Vector2>> _get_outlines() const;
public:
#ifdef TOOLS_ENABLED
#ifdef DEBUG_ENABLED
Rect2 _edit_get_rect() const;
bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const;
#endif
#endif // DEBUG_ENABLED
enum SamplePartitionType {
SAMPLE_PARTITION_CONVEX_PARTITION = 0,
SAMPLE_PARTITION_TRIANGULATE,
SAMPLE_PARTITION_MAX
};
enum ParsedGeometryType {
PARSED_GEOMETRY_MESH_INSTANCES = 0,
@ -91,6 +95,7 @@ public:
real_t agent_radius = 10.0f;
SamplePartitionType partition_type = SAMPLE_PARTITION_CONVEX_PARTITION;
ParsedGeometryType parsed_geometry_type = PARSED_GEOMETRY_BOTH;
uint32_t parsed_collision_mask = 0xFFFFFFFF;
@ -109,6 +114,8 @@ public:
Vector<Vector2> get_outline(int p_idx) const;
void remove_outline(int p_idx);
int get_outline_count() const;
void set_outlines(const Vector<Vector<Vector2>> &p_outlines);
Vector<Vector<Vector2>> get_outlines() const;
void clear_outlines();
#ifndef DISABLE_DEPRECATED
@ -116,10 +123,13 @@ public:
#endif // DISABLE_DEPRECATED
void set_polygons(const Vector<Vector<int>> &p_polygons);
const Vector<Vector<int>> &get_polygons() const;
Vector<Vector<int>> get_polygons() const;
Vector<int> get_polygon(int p_idx);
void clear_polygons();
void set_sample_partition_type(SamplePartitionType p_value);
SamplePartitionType get_sample_partition_type() const;
void set_parsed_geometry_type(ParsedGeometryType p_geometry_type);
ParsedGeometryType get_parsed_geometry_type() const;
@ -155,12 +165,15 @@ public:
void clear();
void set_data(const Vector<Vector2> &p_vertices, const Vector<Vector<int>> &p_polygons);
void set_data(const Vector<Vector2> &p_vertices, const Vector<Vector<int>> &p_polygons, const Vector<Vector<Vector2>> &p_outlines);
void get_data(Vector<Vector2> &r_vertices, Vector<Vector<int>> &r_polygons);
void get_data(Vector<Vector2> &r_vertices, Vector<Vector<int>> &r_polygons, Vector<Vector<Vector2>> &r_outlines);
NavigationPolygon() {}
~NavigationPolygon() {}
};
VARIANT_ENUM_CAST(NavigationPolygon::SamplePartitionType);
VARIANT_ENUM_CAST(NavigationPolygon::ParsedGeometryType);
VARIANT_ENUM_CAST(NavigationPolygon::SourceGeometryMode);

View file

@ -59,6 +59,9 @@ bool RectangleShape2D::_get(const StringName &p_name, Variant &r_property) const
void RectangleShape2D::set_size(const Size2 &p_size) {
ERR_FAIL_COND_MSG(p_size.x < 0 || p_size.y < 0, "RectangleShape2D size cannot be negative.");
if (size == p_size) {
return;
}
size = p_size;
_update_shape();
}

View file

@ -49,6 +49,9 @@ void SegmentShape2D::_update_shape() {
}
void SegmentShape2D::set_a(const Vector2 &p_a) {
if (a == p_a) {
return;
}
a = p_a;
_update_shape();
}
@ -58,6 +61,9 @@ Vector2 SegmentShape2D::get_a() const {
}
void SegmentShape2D::set_b(const Vector2 &p_b) {
if (b == p_b) {
return;
}
b = p_b;
_update_shape();
}

View file

@ -94,6 +94,9 @@ void SeparationRayShape2D::_bind_methods() {
}
void SeparationRayShape2D::set_length(real_t p_length) {
if (length == p_length) {
return;
}
length = p_length;
_update_shape();
}
@ -103,6 +106,9 @@ real_t SeparationRayShape2D::get_length() const {
}
void SeparationRayShape2D::set_slide_on_slope(bool p_active) {
if (slide_on_slope == p_active) {
return;
}
slide_on_slope = p_active;
_update_shape();
}

View file

@ -30,7 +30,6 @@
#include "shape_2d.h"
#include "core/config/engine.h"
#include "core/config/project_settings.h"
#include "servers/physics_server_2d.h"

View file

@ -31,10 +31,6 @@
#include "skeleton_modification_2d.h"
#include "scene/2d/skeleton_2d.h"
#include "scene/2d/physics/collision_object_2d.h"
#include "scene/2d/physics/collision_shape_2d.h"
#include "scene/2d/physics/physical_bone_2d.h"
#ifdef TOOLS_ENABLED
#include "editor/editor_settings.h"
#endif // TOOLS_ENABLED

View file

@ -31,14 +31,12 @@
#ifndef SKELETON_MODIFICATION_2D_H
#define SKELETON_MODIFICATION_2D_H
#include "scene/2d/skeleton_2d.h"
#include "scene/resources/2d/skeleton/skeleton_modification_stack_2d.h"
///////////////////////////////////////
// SkeletonModification2D
///////////////////////////////////////
class SkeletonModificationStack2D;
class Bone2D;
class SkeletonModification2D : public Resource {

View file

@ -31,10 +31,6 @@
#include "skeleton_modification_2d_ccdik.h"
#include "scene/2d/skeleton_2d.h"
#ifdef TOOLS_ENABLED
#include "editor/editor_settings.h"
#endif // TOOLS_ENABLED
bool SkeletonModification2DCCDIK::_set(const StringName &p_path, const Variant &p_value) {
String path = p_path;

View file

@ -31,10 +31,6 @@
#include "skeleton_modification_2d_fabrik.h"
#include "scene/2d/skeleton_2d.h"
#ifdef TOOLS_ENABLED
#include "editor/editor_settings.h"
#endif // TOOLS_ENABLED
bool SkeletonModification2DFABRIK::_set(const StringName &p_path, const Variant &p_value) {
String path = p_path;

View file

@ -31,10 +31,6 @@
#include "skeleton_modification_2d_lookat.h"
#include "scene/2d/skeleton_2d.h"
#ifdef TOOLS_ENABLED
#include "editor/editor_settings.h"
#endif // TOOLS_ENABLED
bool SkeletonModification2DLookAt::_set(const StringName &p_path, const Variant &p_value) {
String path = p_path;

View file

@ -71,7 +71,7 @@ void SkeletonModificationStack2D::setup() {
if (skeleton != nullptr) {
is_setup = true;
for (int i = 0; i < modifications.size(); i++) {
if (!modifications[i].is_valid()) {
if (modifications[i].is_null()) {
continue;
}
modifications.get(i)->_setup_modification(this);
@ -100,7 +100,7 @@ void SkeletonModificationStack2D::execute(float p_delta, int p_execution_mode) {
}
for (int i = 0; i < modifications.size(); i++) {
if (!modifications[i].is_valid()) {
if (modifications[i].is_null()) {
continue;
}
@ -117,7 +117,7 @@ void SkeletonModificationStack2D::draw_editor_gizmos() {
if (editor_gizmo_dirty) {
for (int i = 0; i < modifications.size(); i++) {
if (!modifications[i].is_valid()) {
if (modifications[i].is_null()) {
continue;
}
@ -147,7 +147,7 @@ void SkeletonModificationStack2D::set_editor_gizmos_dirty(bool p_dirty) {
void SkeletonModificationStack2D::enable_all_modifications(bool p_enabled) {
for (int i = 0; i < modifications.size(); i++) {
if (!modifications[i].is_valid()) {
if (modifications[i].is_null()) {
continue;
}
modifications.get(i)->set_enabled(p_enabled);
@ -160,7 +160,7 @@ Ref<SkeletonModification2D> SkeletonModificationStack2D::get_modification(int p_
}
void SkeletonModificationStack2D::add_modification(Ref<SkeletonModification2D> p_mod) {
ERR_FAIL_COND(!p_mod.is_valid());
ERR_FAIL_COND(p_mod.is_null());
p_mod->_setup_modification(this);
modifications.push_back(p_mod);

View file

@ -31,8 +31,7 @@
#ifndef SKELETON_MODIFICATION_STACK_2D_H
#define SKELETON_MODIFICATION_STACK_2D_H
#include "scene/2d/skeleton_2d.h"
#include "scene/resources/2d/skeleton/skeleton_modification_2d.h"
#include "core/io/resource.h"
///////////////////////////////////////
// SkeletonModificationStack2D
@ -64,7 +63,7 @@ public:
execution_mode_physics_process
};
Vector<Ref<SkeletonModification2D>> modifications = Vector<Ref<SkeletonModification2D>>();
Vector<Ref<SkeletonModification2D>> modifications;
void setup();
void execute(float p_delta, int p_execution_mode);

View file

@ -37,7 +37,7 @@ Ref<NavigationPolygon> TileData::_get_navigation_polygon_bind_compat_84660(int p
}
Ref<OccluderPolygon2D> TileData::_get_occluder_bind_compat_84660(int p_layer_id) const {
return get_occluder(p_layer_id, false, false, false);
return get_occluder_polygon(p_layer_id, 0, false, false, false);
}
void TileData::_bind_compatibility_methods() {

View file

@ -174,13 +174,13 @@ void TileMapPattern::set_size(const Size2i &p_size) {
bool TileMapPattern::is_empty() const {
return pattern.is_empty();
};
}
void TileMapPattern::clear() {
size = Size2i();
pattern.clear();
emit_changed();
};
}
bool TileMapPattern::_set(const StringName &p_name, const Variant &p_value) {
if (p_name == "tile_data") {
@ -471,7 +471,7 @@ void TileSet::_compute_next_source_id() {
// Sources management
int TileSet::add_source(Ref<TileSetSource> p_tile_set_source, int p_atlas_source_id_override) {
ERR_FAIL_COND_V(!p_tile_set_source.is_valid(), TileSet::INVALID_SOURCE);
ERR_FAIL_COND_V(p_tile_set_source.is_null(), TileSet::INVALID_SOURCE);
ERR_FAIL_COND_V_MSG(p_atlas_source_id_override >= 0 && (sources.has(p_atlas_source_id_override)), TileSet::INVALID_SOURCE, vformat("Cannot create TileSet atlas source. Another atlas source exists with id %d.", p_atlas_source_id_override));
ERR_FAIL_COND_V_MSG(p_atlas_source_id_override < 0 && p_atlas_source_id_override != TileSet::INVALID_SOURCE, TileSet::INVALID_SOURCE, vformat("Provided source ID %d is not valid. Negative source IDs are not allowed.", p_atlas_source_id_override));
@ -571,11 +571,11 @@ void TileSet::set_uv_clipping(bool p_uv_clipping) {
bool TileSet::is_uv_clipping() const {
return uv_clipping;
};
}
int TileSet::get_occlusion_layers_count() const {
return occlusion_layers.size();
};
}
void TileSet::add_occlusion_layer(int p_index) {
if (p_index < 0) {
@ -699,6 +699,17 @@ uint32_t TileSet::get_physics_layer_collision_mask(int p_layer_index) const {
return physics_layers[p_layer_index].collision_mask;
}
void TileSet::set_physics_layer_collision_priority(int p_layer_index, real_t p_priority) {
ERR_FAIL_INDEX(p_layer_index, physics_layers.size());
physics_layers.write[p_layer_index].collision_priority = p_priority;
emit_changed();
}
real_t TileSet::get_physics_layer_collision_priority(int p_layer_index) const {
ERR_FAIL_INDEX_V(p_layer_index, physics_layers.size(), 0);
return physics_layers[p_layer_index].collision_priority;
}
void TileSet::set_physics_layer_physics_material(int p_layer_index, Ref<PhysicsMaterial> p_physics_material) {
ERR_FAIL_INDEX(p_layer_index, physics_layers.size());
physics_layers.write[p_layer_index].physics_material = p_physics_material;
@ -1111,6 +1122,10 @@ void TileSet::set_custom_data_layer_name(int p_layer_id, String p_value) {
emit_changed();
}
bool TileSet::has_custom_data_layer_by_name(const String &p_value) const {
return custom_data_layers_by_name.has(p_value);
}
String TileSet::get_custom_data_layer_name(int p_layer_id) const {
ERR_FAIL_INDEX_V(p_layer_id, custom_data_layers.size(), "");
return custom_data_layers[p_layer_id].name;
@ -1381,7 +1396,7 @@ void TileSet::clear_tile_proxies() {
}
int TileSet::add_pattern(Ref<TileMapPattern> p_pattern, int p_index) {
ERR_FAIL_COND_V(!p_pattern.is_valid(), -1);
ERR_FAIL_COND_V(p_pattern.is_null(), -1);
ERR_FAIL_COND_V_MSG(p_pattern->is_empty(), -1, "Cannot add an empty pattern to the TileSet.");
for (const Ref<TileMapPattern> &pattern : patterns) {
ERR_FAIL_COND_V_MSG(pattern == p_pattern, -1, "TileSet has already this pattern.");
@ -3444,7 +3459,8 @@ void TileSet::_compatibility_conversion() {
polygon.write[index] = xform.xform(polygon[index] - ctd->region.get_size() / 2.0);
}
occluder->set_polygon(polygon);
tile_data->set_occluder(0, occluder);
tile_data->add_occluder_polygon(0);
tile_data->set_occluder_polygon(0, 0, occluder);
}
if (ctd->navigation.is_valid()) {
if (get_navigation_layers_count() < 1) {
@ -3558,7 +3574,8 @@ void TileSet::_compatibility_conversion() {
polygon.write[index] = xform.xform(polygon[index] - ctd->region.get_size() / 2.0);
}
occluder->set_polygon(polygon);
tile_data->set_occluder(0, occluder);
tile_data->add_occluder_polygon(0);
tile_data->set_occluder_polygon(0, 0, occluder);
}
if (ctd->autotile_navpoly_map.has(coords)) {
if (get_navigation_layers_count() < 1) {
@ -3689,7 +3706,7 @@ Array TileSet::compatibility_tilemap_map(int p_tile_id, Vector2i p_coords, bool
return cannot_convert_array;
break;
}
};
}
#endif // DISABLE_DEPRECATED
@ -3898,6 +3915,13 @@ bool TileSet::_set(const StringName &p_name, const Variant &p_value) {
}
set_physics_layer_collision_mask(index, p_value);
return true;
} else if (components[1] == "collision_priority") {
ERR_FAIL_COND_V(p_value.get_type() != Variant::FLOAT, false);
while (index >= physics_layers.size()) {
add_physics_layer();
}
set_physics_layer_collision_priority(index, p_value);
return true;
} else if (components[1] == "physics_material") {
Ref<PhysicsMaterial> physics_material = p_value;
while (index >= physics_layers.size()) {
@ -3920,7 +3944,7 @@ bool TileSet::_set(const StringName &p_name, const Variant &p_value) {
int terrain_index = components[1].trim_prefix("terrain_").to_int();
ERR_FAIL_COND_V(terrain_index < 0, false);
if (components[2] == "name") {
ERR_FAIL_COND_V(p_value.get_type() != Variant::STRING, false);
ERR_FAIL_COND_V(!p_value.is_string(), false);
while (terrain_set_index >= terrain_sets.size()) {
add_terrain_set();
}
@ -3958,7 +3982,7 @@ bool TileSet::_set(const StringName &p_name, const Variant &p_value) {
int index = components[0].trim_prefix("custom_data_layer_").to_int();
ERR_FAIL_COND_V(index < 0, false);
if (components[1] == "name") {
ERR_FAIL_COND_V(p_value.get_type() != Variant::STRING, false);
ERR_FAIL_COND_V(!p_value.is_string(), false);
while (index >= custom_data_layers.size()) {
add_custom_data_layer();
}
@ -4049,6 +4073,9 @@ bool TileSet::_get(const StringName &p_name, Variant &r_ret) const {
} else if (components[1] == "collision_mask") {
r_ret = get_physics_layer_collision_mask(index);
return true;
} else if (components[1] == "collision_priority") {
r_ret = get_physics_layer_collision_priority(index);
return true;
} else if (components[1] == "physics_material") {
r_ret = get_physics_layer_physics_material(index);
return true;
@ -4174,9 +4201,16 @@ void TileSet::_get_property_list(List<PropertyInfo> *p_list) const {
}
p_list->push_back(property_info);
// physics_layer_%d/collision_priority
property_info = PropertyInfo(Variant::FLOAT, vformat("physics_layer_%d/collision_priority", i));
if (physics_layers[i].collision_priority == 1.0) {
property_info.usage ^= PROPERTY_USAGE_STORAGE;
}
p_list->push_back(property_info);
// physics_layer_%d/physics_material
property_info = PropertyInfo(Variant::OBJECT, vformat("physics_layer_%d/physics_material", i), PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial");
if (!physics_layers[i].physics_material.is_valid()) {
if (physics_layers[i].physics_material.is_null()) {
property_info.usage ^= PROPERTY_USAGE_STORAGE;
}
p_list->push_back(property_info);
@ -4218,10 +4252,10 @@ void TileSet::_get_property_list(List<PropertyInfo> *p_list) const {
// Tile Proxies.
// Note: proxies need to be set after sources are set.
p_list->push_back(PropertyInfo(Variant::NIL, GNAME("Tile Proxies", ""), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP));
p_list->push_back(PropertyInfo(Variant::ARRAY, PNAME("tile_proxies/source_level"), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
p_list->push_back(PropertyInfo(Variant::ARRAY, PNAME("tile_proxies/coords_level"), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
p_list->push_back(PropertyInfo(Variant::ARRAY, PNAME("tile_proxies/alternative_level"), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
p_list->push_back(PropertyInfo(Variant::NIL, "Tile Proxies", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP));
p_list->push_back(PropertyInfo(Variant::ARRAY, "tile_proxies/source_level", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
p_list->push_back(PropertyInfo(Variant::ARRAY, "tile_proxies/coords_level", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
p_list->push_back(PropertyInfo(Variant::ARRAY, "tile_proxies/alternative_level", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
// Patterns.
for (unsigned int pattern_index = 0; pattern_index < patterns.size(); pattern_index++) {
@ -4285,6 +4319,8 @@ void TileSet::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_physics_layer_collision_layer", "layer_index"), &TileSet::get_physics_layer_collision_layer);
ClassDB::bind_method(D_METHOD("set_physics_layer_collision_mask", "layer_index", "mask"), &TileSet::set_physics_layer_collision_mask);
ClassDB::bind_method(D_METHOD("get_physics_layer_collision_mask", "layer_index"), &TileSet::get_physics_layer_collision_mask);
ClassDB::bind_method(D_METHOD("set_physics_layer_collision_priority", "layer_index", "priority"), &TileSet::set_physics_layer_collision_priority);
ClassDB::bind_method(D_METHOD("get_physics_layer_collision_priority", "layer_index"), &TileSet::get_physics_layer_collision_priority);
ClassDB::bind_method(D_METHOD("set_physics_layer_physics_material", "layer_index", "physics_material"), &TileSet::set_physics_layer_physics_material);
ClassDB::bind_method(D_METHOD("get_physics_layer_physics_material", "layer_index"), &TileSet::get_physics_layer_physics_material);
@ -4322,6 +4358,7 @@ void TileSet::_bind_methods() {
ClassDB::bind_method(D_METHOD("remove_custom_data_layer", "layer_index"), &TileSet::remove_custom_data_layer);
ClassDB::bind_method(D_METHOD("get_custom_data_layer_by_name", "layer_name"), &TileSet::get_custom_data_layer_by_name);
ClassDB::bind_method(D_METHOD("set_custom_data_layer_name", "layer_index", "layer_name"), &TileSet::set_custom_data_layer_name);
ClassDB::bind_method(D_METHOD("has_custom_data_layer_by_name", "layer_name"), &TileSet::has_custom_data_layer_by_name);
ClassDB::bind_method(D_METHOD("get_custom_data_layer_name", "layer_index"), &TileSet::get_custom_data_layer_name);
ClassDB::bind_method(D_METHOD("set_custom_data_layer_type", "layer_index", "layer_type"), &TileSet::set_custom_data_layer_type);
ClassDB::bind_method(D_METHOD("get_custom_data_layer_type", "layer_index"), &TileSet::get_custom_data_layer_type);
@ -4430,7 +4467,7 @@ TileSet *TileSetSource::get_tile_set() const {
void TileSetSource::reset_state() {
tile_set = nullptr;
};
}
void TileSetSource::_bind_methods() {
// Base tiles
@ -4710,7 +4747,7 @@ bool TileSetAtlasSource::get_use_texture_padding() const {
Vector2i TileSetAtlasSource::get_atlas_grid_size() const {
Ref<Texture2D> txt = get_texture();
if (!txt.is_valid()) {
if (txt.is_null()) {
return Vector2i();
}
@ -4929,10 +4966,13 @@ void TileSetAtlasSource::_get_property_list(List<PropertyInfo> *p_list) const {
}
for (const KeyValue<int, TileData *> &E_alternative : E_tile.value.alternatives) {
const String formatted_key = itos(E_alternative.key);
// Add a dummy property to show the alternative exists.
tile_property_list.push_back(PropertyInfo(Variant::INT, vformat("%d", E_alternative.key), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
tile_property_list.push_back(PropertyInfo(Variant::INT, formatted_key, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
// Get the alternative tile's properties and append them to the list of properties.
const String alternative_property_info_prefix = formatted_key + '/';
List<PropertyInfo> alternative_property_list;
E_alternative.value->get_property_list(&alternative_property_list);
for (PropertyInfo &alternative_property_info : alternative_property_list) {
@ -4941,14 +4981,15 @@ void TileSetAtlasSource::_get_property_list(List<PropertyInfo> *p_list) const {
if (default_value.get_type() != Variant::NIL && bool(Variant::evaluate(Variant::OP_EQUAL, value, default_value))) {
alternative_property_info.usage ^= PROPERTY_USAGE_STORAGE;
}
alternative_property_info.name = vformat("%s/%s", vformat("%d", E_alternative.key), alternative_property_info.name);
alternative_property_info.name = alternative_property_info_prefix + alternative_property_info.name;
tile_property_list.push_back(alternative_property_info);
}
}
// Add all alternative.
const String property_info_prefix = vformat("%d:%d/", E_tile.key.x, E_tile.key.y);
for (PropertyInfo &tile_property_info : tile_property_list) {
tile_property_info.name = vformat("%s/%s", vformat("%d:%d", E_tile.key.x, E_tile.key.y), tile_property_info.name);
tile_property_info.name = property_info_prefix + tile_property_info.name;
p_list->push_back(tile_property_info);
}
}
@ -5275,11 +5316,26 @@ Rect2i TileSetAtlasSource::get_tile_texture_region(Vector2i p_atlas_coords, int
bool TileSetAtlasSource::is_position_in_tile_texture_region(const Vector2i p_atlas_coords, int p_alternative_tile, Vector2 p_position) const {
Size2 size = get_tile_texture_region(p_atlas_coords).size;
Rect2 rect = Rect2(-size / 2 - get_tile_data(p_atlas_coords, p_alternative_tile)->get_texture_origin(), size);
TileData *tile_data = get_tile_data(p_atlas_coords, p_alternative_tile);
if (tile_data->get_transpose()) {
size = Size2(size.y, size.x);
}
Rect2 rect = Rect2(-size / 2 - tile_data->get_texture_origin(), size);
return rect.has_point(p_position);
}
bool TileSetAtlasSource::is_rect_in_tile_texture_region(const Vector2i p_atlas_coords, int p_alternative_tile, Rect2 p_rect) const {
Size2 size = get_tile_texture_region(p_atlas_coords).size;
TileData *tile_data = get_tile_data(p_atlas_coords, p_alternative_tile);
if (tile_data->get_transpose()) {
size = Size2(size.y, size.x);
}
Rect2 rect = Rect2(-size / 2 - tile_data->get_texture_origin(), size);
return p_rect.intersection(rect) == p_rect;
}
int TileSetAtlasSource::alternative_no_transform(int p_alternative_id) {
return p_alternative_id & ~(TRANSFORM_FLIP_H | TRANSFORM_FLIP_V | TRANSFORM_TRANSPOSE);
}
@ -6205,33 +6261,86 @@ int TileData::get_y_sort_origin() const {
return y_sort_origin;
}
#ifndef DISABLE_DEPRECATED
void TileData::set_occluder(int p_layer_id, Ref<OccluderPolygon2D> p_occluder_polygon) {
ERR_FAIL_INDEX(p_layer_id, occluders.size());
occluders.write[p_layer_id].occluder = p_occluder_polygon;
occluders.write[p_layer_id].transformed_occluders.clear();
if (get_occluder_polygons_count(p_layer_id) == 0) {
add_occluder_polygon(p_layer_id);
}
set_occluder_polygon(p_layer_id, 0, p_occluder_polygon);
emit_signal(CoreStringName(changed));
}
Ref<OccluderPolygon2D> TileData::get_occluder(int p_layer_id, bool p_flip_h, bool p_flip_v, bool p_transpose) const {
ERR_FAIL_INDEX_V(p_layer_id, occluders.size(), Ref<OccluderPolygon2D>());
if (get_occluder_polygons_count(p_layer_id) == 0) {
return Ref<OccluderPolygon2D>();
}
return get_occluder_polygon(p_layer_id, 0, p_flip_h, p_flip_v, p_transpose);
}
#endif // DISABLE_DEPRECATED
void TileData::set_occluder_polygons_count(int p_layer_id, int p_polygons_count) {
ERR_FAIL_INDEX(p_layer_id, occluders.size());
ERR_FAIL_COND(p_polygons_count < 0);
if (p_polygons_count == occluders.write[p_layer_id].polygons.size()) {
return;
}
occluders.write[p_layer_id].polygons.resize(p_polygons_count);
notify_property_list_changed();
emit_signal(CoreStringName(changed));
}
int TileData::get_occluder_polygons_count(int p_layer_id) const {
ERR_FAIL_INDEX_V(p_layer_id, occluders.size(), 0);
return occluders[p_layer_id].polygons.size();
}
void TileData::add_occluder_polygon(int p_layer_id) {
ERR_FAIL_INDEX(p_layer_id, occluders.size());
occluders.write[p_layer_id].polygons.push_back(OcclusionLayerTileData::PolygonOccluderTileData());
emit_signal(CoreStringName(changed));
}
void TileData::remove_occluder_polygon(int p_layer_id, int p_polygon_index) {
ERR_FAIL_INDEX(p_layer_id, occluders.size());
ERR_FAIL_INDEX(p_polygon_index, occluders[p_layer_id].polygons.size());
occluders.write[p_layer_id].polygons.remove_at(p_polygon_index);
emit_signal(CoreStringName(changed));
}
void TileData::set_occluder_polygon(int p_layer_id, int p_polygon_index, const Ref<OccluderPolygon2D> &p_occluder_polygon) {
ERR_FAIL_INDEX(p_layer_id, occluders.size());
ERR_FAIL_INDEX(p_polygon_index, occluders[p_layer_id].polygons.size());
OcclusionLayerTileData::PolygonOccluderTileData &polygon_occluder_tile_data = occluders.write[p_layer_id].polygons.write[p_polygon_index];
polygon_occluder_tile_data.occluder_polygon = p_occluder_polygon;
polygon_occluder_tile_data.transformed_polygon_occluders.clear();
emit_signal(CoreStringName(changed));
}
Ref<OccluderPolygon2D> TileData::get_occluder_polygon(int p_layer_id, int p_polygon_index, bool p_flip_h, bool p_flip_v, bool p_transpose) const {
ERR_FAIL_INDEX_V(p_layer_id, occluders.size(), Ref<OccluderPolygon2D>());
ERR_FAIL_INDEX_V(p_polygon_index, occluders[p_layer_id].polygons.size(), Ref<OccluderPolygon2D>());
const OcclusionLayerTileData &layer_tile_data = occluders[p_layer_id];
const Ref<OccluderPolygon2D> &occluder_polygon = layer_tile_data.polygons[p_polygon_index].occluder_polygon;
int key = int(p_flip_h) | int(p_flip_v) << 1 | int(p_transpose) << 2;
if (key == 0) {
return layer_tile_data.occluder;
return occluder_polygon;
}
if (layer_tile_data.occluder.is_null()) {
if (occluder_polygon.is_null()) {
return Ref<OccluderPolygon2D>();
}
HashMap<int, Ref<OccluderPolygon2D>>::Iterator I = layer_tile_data.transformed_occluders.find(key);
HashMap<int, Ref<OccluderPolygon2D>>::Iterator I = layer_tile_data.polygons[p_polygon_index].transformed_polygon_occluders.find(key);
if (!I) {
Ref<OccluderPolygon2D> transformed_polygon;
transformed_polygon.instantiate();
transformed_polygon->set_polygon(get_transformed_vertices(layer_tile_data.occluder->get_polygon(), p_flip_h, p_flip_v, p_transpose));
layer_tile_data.transformed_occluders[key] = transformed_polygon;
transformed_polygon->set_polygon(get_transformed_vertices(occluder_polygon->get_polygon(), p_flip_h, p_flip_v, p_transpose));
layer_tile_data.polygons[p_polygon_index].transformed_polygon_occluders[key] = transformed_polygon;
return transformed_polygon;
} else {
return I->value;
@ -6410,9 +6519,9 @@ int TileData::get_terrain_set() const {
}
void TileData::set_terrain(int p_terrain) {
ERR_FAIL_COND(terrain_set < 0);
ERR_FAIL_COND(p_terrain < -1);
if (tile_set) {
ERR_FAIL_COND(terrain_set < 0 && p_terrain != -1);
if (tile_set && terrain_set >= 0) {
ERR_FAIL_COND(p_terrain >= tile_set->get_terrains_count(terrain_set));
}
terrain = p_terrain;
@ -6425,9 +6534,9 @@ int TileData::get_terrain() const {
void TileData::set_terrain_peering_bit(TileSet::CellNeighbor p_peering_bit, int p_terrain_index) {
ERR_FAIL_INDEX(p_peering_bit, TileSet::CellNeighbor::CELL_NEIGHBOR_MAX);
ERR_FAIL_COND(terrain_set < 0);
ERR_FAIL_COND(p_terrain_index < -1);
if (tile_set) {
ERR_FAIL_COND(terrain_set < 0 && p_terrain_index != -1);
if (tile_set && terrain_set >= 0) {
ERR_FAIL_COND(p_terrain_index >= tile_set->get_terrains_count(terrain_set));
ERR_FAIL_COND(!is_valid_terrain_peering_bit(p_peering_bit));
}
@ -6487,17 +6596,18 @@ Ref<NavigationPolygon> TileData::get_navigation_polygon(int p_layer_id, bool p_f
transformed_polygon.instantiate();
PackedVector2Array new_points = get_transformed_vertices(layer_tile_data.navigation_polygon->get_vertices(), p_flip_h, p_flip_v, p_transpose);
transformed_polygon->set_vertices(new_points);
int num_polygons = layer_tile_data.navigation_polygon->get_polygon_count();
for (int i = 0; i < num_polygons; ++i) {
transformed_polygon->add_polygon(layer_tile_data.navigation_polygon->get_polygon(i));
const Vector<Vector<Vector2>> outlines = layer_tile_data.navigation_polygon->get_outlines();
int outline_count = outlines.size();
Vector<Vector<Vector2>> new_outlines;
new_outlines.resize(outline_count);
for (int i = 0; i < outline_count; i++) {
new_outlines.write[i] = get_transformed_vertices(outlines[i], p_flip_h, p_flip_v, p_transpose);
}
for (int i = 0; i < layer_tile_data.navigation_polygon->get_outline_count(); i++) {
PackedVector2Array new_outline = get_transformed_vertices(layer_tile_data.navigation_polygon->get_outline(i), p_flip_h, p_flip_v, p_transpose);
transformed_polygon->add_outline(new_outline);
}
transformed_polygon->set_data(new_points, layer_tile_data.navigation_polygon->get_polygons(), new_outlines);
layer_tile_data.transformed_navigation_polygon[key] = transformed_polygon;
return transformed_polygon;
@ -6531,6 +6641,11 @@ Variant TileData::get_custom_data(String p_layer_name) const {
return get_custom_data_by_layer_id(p_layer_id);
}
bool TileData::has_custom_data(const String &p_layer_name) const {
ERR_FAIL_NULL_V(tile_set, false);
return tile_set->has_custom_data_layer_by_name(p_layer_name);
}
void TileData::set_custom_data_by_layer_id(int p_layer_id, Variant p_value) {
ERR_FAIL_INDEX(p_layer_id, custom_data.size());
custom_data.write[p_layer_id] = p_value;
@ -6578,13 +6693,37 @@ bool TileData::_set(const StringName &p_name, const Variant &p_value) {
#endif
Vector<String> components = String(p_name).split("/", true, 2);
if (components.size() == 2 && components[0].begins_with("occlusion_layer_") && components[0].trim_prefix("occlusion_layer_").is_valid_int()) {
if (components.size() >= 2 && components[0].begins_with("occlusion_layer_") && components[0].trim_prefix("occlusion_layer_").is_valid_int()) {
// Occlusion layers.
int layer_index = components[0].trim_prefix("occlusion_layer_").to_int();
ERR_FAIL_COND_V(layer_index < 0, false);
if (components[1] == "polygon") {
Ref<OccluderPolygon2D> polygon = p_value;
if (components.size() == 2) {
if (components[1] == "polygon") {
// Kept for compatibility.
Ref<OccluderPolygon2D> polygon = p_value;
if (layer_index >= occluders.size()) {
if (tile_set) {
return false;
} else {
occluders.resize(layer_index + 1);
}
}
if (get_occluder_polygons_count(layer_index) == 0) {
add_occluder_polygon(layer_index);
}
set_occluder_polygon(layer_index, 0, polygon);
return true;
} else if (components[1] == "polygons_count") {
if (p_value.get_type() != Variant::INT) {
return false;
}
set_occluder_polygons_count(layer_index, p_value);
return true;
}
} else if (components.size() == 3 && components[1].begins_with("polygon_") && components[1].trim_prefix("polygon_").is_valid_int()) {
// Polygons.
int polygon_index = components[1].trim_prefix("polygon_").to_int();
ERR_FAIL_COND_V(polygon_index < 0, false);
if (layer_index >= occluders.size()) {
if (tile_set) {
@ -6593,8 +6732,16 @@ bool TileData::_set(const StringName &p_name, const Variant &p_value) {
occluders.resize(layer_index + 1);
}
}
set_occluder(layer_index, polygon);
return true;
if (polygon_index >= occluders[layer_index].polygons.size()) {
occluders.write[layer_index].polygons.resize(polygon_index + 1);
}
if (components[2] == "polygon") {
Ref<OccluderPolygon2D> polygon = p_value;
set_occluder_polygon(layer_index, polygon_index, polygon);
return true;
}
}
} else if (components.size() >= 2 && components[0].begins_with("physics_layer_") && components[0].trim_prefix("physics_layer_").is_valid_int()) {
// Physics layers.
@ -6622,6 +6769,7 @@ bool TileData::_set(const StringName &p_name, const Variant &p_value) {
return true;
}
} else if (components.size() == 3 && components[1].begins_with("polygon_") && components[1].trim_prefix("polygon_").is_valid_int()) {
// Polygons.
int polygon_index = components[1].trim_prefix("polygon_").to_int();
ERR_FAIL_COND_V(polygon_index < 0, false);
@ -6708,16 +6856,36 @@ bool TileData::_get(const StringName &p_name, Variant &r_ret) const {
Vector<String> components = String(p_name).split("/", true, 2);
if (tile_set) {
if (components.size() == 2 && components[0].begins_with("occlusion_layer") && components[0].trim_prefix("occlusion_layer_").is_valid_int()) {
if (components.size() >= 2 && components[0].begins_with("occlusion_layer") && components[0].trim_prefix("occlusion_layer_").is_valid_int()) {
// Occlusion layers.
int layer_index = components[0].trim_prefix("occlusion_layer_").to_int();
ERR_FAIL_COND_V(layer_index < 0, false);
if (layer_index >= occluders.size()) {
return false;
}
if (components[1] == "polygon") {
r_ret = get_occluder(layer_index);
return true;
if (components.size() == 2) {
if (components[1] == "polygon") {
// Kept for compatibility.
if (occluders[layer_index].polygons.is_empty()) {
return false;
}
r_ret = get_occluder_polygon(layer_index, 0);
return true;
} else if (components[1] == "polygons_count") {
r_ret = get_occluder_polygons_count(layer_index);
return true;
}
} else if (components.size() == 3 && components[1].begins_with("polygon_") && components[1].trim_prefix("polygon_").is_valid_int()) {
// Polygons.
int polygon_index = components[1].trim_prefix("polygon_").to_int();
ERR_FAIL_COND_V(polygon_index < 0, false);
if (polygon_index >= occluders[layer_index].polygons.size()) {
return false;
}
if (components[2] == "polygon") {
r_ret = get_occluder_polygon(layer_index, polygon_index);
return true;
}
}
} else if (components.size() >= 2 && components[0].begins_with("physics_layer_") && components[0].trim_prefix("physics_layer_").is_valid_int()) {
// Physics layers.
@ -6797,12 +6965,15 @@ void TileData::_get_property_list(List<PropertyInfo> *p_list) const {
// Occlusion layers.
p_list->push_back(PropertyInfo(Variant::NIL, GNAME("Rendering", ""), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP));
for (int i = 0; i < occluders.size(); i++) {
// occlusion_layer_%d/polygon
property_info = PropertyInfo(Variant::OBJECT, vformat("occlusion_layer_%d/%s", i, PNAME("polygon")), PROPERTY_HINT_RESOURCE_TYPE, "OccluderPolygon2D", PROPERTY_USAGE_DEFAULT);
if (occluders[i].occluder.is_null()) {
property_info.usage ^= PROPERTY_USAGE_STORAGE;
p_list->push_back(PropertyInfo(Variant::INT, vformat("occlusion_layer_%d/%s", i, PNAME("polygons_count")), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR));
for (int j = 0; j < occluders[i].polygons.size(); j++) {
// occlusion_layer_%d/polygon_%d/polygon
property_info = PropertyInfo(Variant::OBJECT, vformat("occlusion_layer_%d/polygon_%d/%s", i, j, PNAME("polygon")), PROPERTY_HINT_RESOURCE_TYPE, "OccluderPolygon2D", PROPERTY_USAGE_DEFAULT);
if (occluders[i].polygons[j].occluder_polygon.is_null()) {
property_info.usage ^= PROPERTY_USAGE_STORAGE;
}
p_list->push_back(property_info);
}
p_list->push_back(property_info);
}
// Physics layers.
@ -6907,8 +7078,17 @@ void TileData::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_y_sort_origin", "y_sort_origin"), &TileData::set_y_sort_origin);
ClassDB::bind_method(D_METHOD("get_y_sort_origin"), &TileData::get_y_sort_origin);
ClassDB::bind_method(D_METHOD("set_occluder_polygons_count", "layer_id", "polygons_count"), &TileData::set_occluder_polygons_count);
ClassDB::bind_method(D_METHOD("get_occluder_polygons_count", "layer_id"), &TileData::get_occluder_polygons_count);
ClassDB::bind_method(D_METHOD("add_occluder_polygon", "layer_id"), &TileData::add_occluder_polygon);
ClassDB::bind_method(D_METHOD("remove_occluder_polygon", "layer_id", "polygon_index"), &TileData::remove_occluder_polygon);
ClassDB::bind_method(D_METHOD("set_occluder_polygon", "layer_id", "polygon_index", "polygon"), &TileData::set_occluder_polygon);
ClassDB::bind_method(D_METHOD("get_occluder_polygon", "layer_id", "polygon_index", "flip_h", "flip_v", "transpose"), &TileData::get_occluder_polygon, DEFVAL(false), DEFVAL(false), DEFVAL(false));
#ifndef DISABLE_DEPRECATED
ClassDB::bind_method(D_METHOD("set_occluder", "layer_id", "occluder_polygon"), &TileData::set_occluder);
ClassDB::bind_method(D_METHOD("get_occluder", "layer_id", "flip_h", "flip_v", "transpose"), &TileData::get_occluder, DEFVAL(false), DEFVAL(false), DEFVAL(false));
#endif // DISABLE_DEPRECATED
// Physics.
ClassDB::bind_method(D_METHOD("set_constant_linear_velocity", "layer_id", "velocity"), &TileData::set_constant_linear_velocity);
@ -6946,6 +7126,7 @@ void TileData::_bind_methods() {
// Custom data.
ClassDB::bind_method(D_METHOD("set_custom_data", "layer_name", "value"), &TileData::set_custom_data);
ClassDB::bind_method(D_METHOD("get_custom_data", "layer_name"), &TileData::get_custom_data);
ClassDB::bind_method(D_METHOD("has_custom_data", "layer_name"), &TileData::has_custom_data);
ClassDB::bind_method(D_METHOD("set_custom_data_by_layer_id", "layer_id", "value"), &TileData::set_custom_data_by_layer_id);
ClassDB::bind_method(D_METHOD("get_custom_data_by_layer_id", "layer_id"), &TileData::get_custom_data_by_layer_id);

View file

@ -278,7 +278,7 @@ public:
bool operator==(const TerrainsPattern &p_terrains_pattern) const;
bool operator!=(const TerrainsPattern &p_terrains_pattern) const {
return !operator==(p_terrains_pattern);
};
}
void set_terrain(int p_terrain);
int get_terrain() const;
@ -327,6 +327,7 @@ private:
struct PhysicsLayer {
uint32_t collision_layer = 1;
uint32_t collision_mask = 1;
real_t collision_priority = 1.0;
Ref<PhysicsMaterial> physics_material;
};
Vector<PhysicsLayer> physics_layers;
@ -448,6 +449,8 @@ public:
uint32_t get_physics_layer_collision_layer(int p_layer_index) const;
void set_physics_layer_collision_mask(int p_layer_index, uint32_t p_mask);
uint32_t get_physics_layer_collision_mask(int p_layer_index) const;
void set_physics_layer_collision_priority(int p_layer_index, real_t p_priority);
real_t get_physics_layer_collision_priority(int p_layer_index) const;
void set_physics_layer_physics_material(int p_layer_index, Ref<PhysicsMaterial> p_physics_material);
Ref<PhysicsMaterial> get_physics_layer_physics_material(int p_layer_index) const;
@ -488,6 +491,7 @@ public:
void remove_custom_data_layer(int p_index);
int get_custom_data_layer_by_name(String p_value) const;
void set_custom_data_layer_name(int p_layer_id, String p_value);
bool has_custom_data_layer_by_name(const String &p_value) const;
String get_custom_data_layer_name(int p_layer_id) const;
void set_custom_data_layer_type(int p_layer_id, Variant::Type p_value);
Variant::Type get_custom_data_layer_type(int p_layer_id) const;
@ -571,25 +575,25 @@ public:
// Not exposed.
virtual void set_tile_set(const TileSet *p_tile_set);
TileSet *get_tile_set() const;
virtual void notify_tile_data_properties_should_change(){};
virtual void add_occlusion_layer(int p_index){};
virtual void move_occlusion_layer(int p_from_index, int p_to_pos){};
virtual void remove_occlusion_layer(int p_index){};
virtual void add_physics_layer(int p_index){};
virtual void move_physics_layer(int p_from_index, int p_to_pos){};
virtual void remove_physics_layer(int p_index){};
virtual void add_terrain_set(int p_index){};
virtual void move_terrain_set(int p_from_index, int p_to_pos){};
virtual void remove_terrain_set(int p_index){};
virtual void add_terrain(int p_terrain_set, int p_index){};
virtual void move_terrain(int p_terrain_set, int p_from_index, int p_to_pos){};
virtual void remove_terrain(int p_terrain_set, int p_index){};
virtual void add_navigation_layer(int p_index){};
virtual void move_navigation_layer(int p_from_index, int p_to_pos){};
virtual void remove_navigation_layer(int p_index){};
virtual void add_custom_data_layer(int p_index){};
virtual void move_custom_data_layer(int p_from_index, int p_to_pos){};
virtual void remove_custom_data_layer(int p_index){};
virtual void notify_tile_data_properties_should_change() {}
virtual void add_occlusion_layer(int p_index) {}
virtual void move_occlusion_layer(int p_from_index, int p_to_pos) {}
virtual void remove_occlusion_layer(int p_index) {}
virtual void add_physics_layer(int p_index) {}
virtual void move_physics_layer(int p_from_index, int p_to_pos) {}
virtual void remove_physics_layer(int p_index) {}
virtual void add_terrain_set(int p_index) {}
virtual void move_terrain_set(int p_from_index, int p_to_pos) {}
virtual void remove_terrain_set(int p_index) {}
virtual void add_terrain(int p_terrain_set, int p_index) {}
virtual void move_terrain(int p_terrain_set, int p_from_index, int p_to_pos) {}
virtual void remove_terrain(int p_terrain_set, int p_index) {}
virtual void add_navigation_layer(int p_index) {}
virtual void move_navigation_layer(int p_from_index, int p_to_pos) {}
virtual void remove_navigation_layer(int p_index) {}
virtual void add_custom_data_layer(int p_index) {}
virtual void move_custom_data_layer(int p_from_index, int p_to_pos) {}
virtual void remove_custom_data_layer(int p_index) {}
virtual void reset_state() override;
// Tiles.
@ -763,6 +767,7 @@ public:
Vector2i get_atlas_grid_size() const;
Rect2i get_tile_texture_region(Vector2i p_atlas_coords, int p_frame = 0) const;
bool is_position_in_tile_texture_region(const Vector2i p_atlas_coords, int p_alternative_tile, Vector2 p_position) const;
bool is_rect_in_tile_texture_region(const Vector2i p_atlas_coords, int p_alternative_tile, Rect2 p_rect) const;
static int alternative_no_transform(int p_alternative_id);
@ -811,8 +816,8 @@ public:
// Scenes accessors. Lot are similar to "Alternative tiles".
int get_scene_tiles_count() { return get_alternative_tiles_count(Vector2i()); }
int get_scene_tile_id(int p_index) { return get_alternative_tile_id(Vector2i(), p_index); };
bool has_scene_tile_id(int p_id) { return has_alternative_tile(Vector2i(), p_id); };
int get_scene_tile_id(int p_index) { return get_alternative_tile_id(Vector2i(), p_index); }
bool has_scene_tile_id(int p_id) { return has_alternative_tile(Vector2i(), p_id); }
int create_scene_tile(Ref<PackedScene> p_packed_scene = Ref<PackedScene>(), int p_id_override = -1);
void set_scene_tile_id(int p_id, int p_new_id);
void set_scene_tile_scene(int p_id, Ref<PackedScene> p_packed_scene);
@ -835,13 +840,16 @@ private:
bool flip_v = false;
bool transpose = false;
Vector2i texture_origin;
Ref<Material> material = Ref<Material>();
Ref<Material> material;
Color modulate = Color(1.0, 1.0, 1.0, 1.0);
int z_index = 0;
int y_sort_origin = 0;
struct OcclusionLayerTileData {
Ref<OccluderPolygon2D> occluder;
mutable HashMap<int, Ref<OccluderPolygon2D>> transformed_occluders;
struct PolygonOccluderTileData {
Ref<OccluderPolygon2D> occluder_polygon;
mutable HashMap<int, Ref<OccluderPolygon2D>> transformed_polygon_occluders;
};
Vector<PolygonOccluderTileData> polygons;
};
Vector<OcclusionLayerTileData> occluders;
@ -940,8 +948,17 @@ public:
void set_y_sort_origin(int p_y_sort_origin);
int get_y_sort_origin() const;
#ifndef DISABLE_DEPRECATED
void set_occluder(int p_layer_id, Ref<OccluderPolygon2D> p_occluder_polygon);
Ref<OccluderPolygon2D> get_occluder(int p_layer_id, bool p_flip_h = false, bool p_flip_v = false, bool p_transpose = false) const;
#endif // DISABLE_DEPRECATED
void set_occluder_polygons_count(int p_layer_id, int p_polygons_count);
int get_occluder_polygons_count(int p_layer_id) const;
void add_occluder_polygon(int p_layer_id);
void remove_occluder_polygon(int p_layer_id, int p_polygon_index);
void set_occluder_polygon(int p_layer_id, int p_polygon_index, const Ref<OccluderPolygon2D> &p_occluder_polygon);
Ref<OccluderPolygon2D> get_occluder_polygon(int p_layer_id, int p_polygon_index, bool p_flip_h = false, bool p_flip_v = false, bool p_transpose = false) const;
// Physics
void set_constant_linear_velocity(int p_layer_id, const Vector2 &p_velocity);
@ -983,6 +1000,7 @@ public:
// Custom data.
void set_custom_data(String p_layer_name, Variant p_value);
Variant get_custom_data(String p_layer_name) const;
bool has_custom_data(const String &p_layer_name) const;
void set_custom_data_by_layer_id(int p_layer_id, Variant p_value);
Variant get_custom_data_by_layer_id(int p_layer_id) const;

View file

@ -1,4 +1,5 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
Import("env")

View file

@ -29,6 +29,8 @@
/**************************************************************************/
#include "box_shape_3d.h"
#include "scene/resources/3d/primitive_meshes.h"
#include "servers/physics_server_3d.h"
Vector<Vector3> BoxShape3D::get_debug_mesh_lines() const {
@ -47,6 +49,24 @@ Vector<Vector3> BoxShape3D::get_debug_mesh_lines() const {
return lines;
}
Ref<ArrayMesh> BoxShape3D::get_debug_arraymesh_faces(const Color &p_modulate) const {
Array box_array;
box_array.resize(RS::ARRAY_MAX);
BoxMesh::create_mesh_array(box_array, size);
Vector<Color> colors;
const PackedVector3Array &verts = box_array[RS::ARRAY_VERTEX];
const int32_t verts_size = verts.size();
for (int i = 0; i < verts_size; i++) {
colors.append(p_modulate);
}
Ref<ArrayMesh> box_mesh = memnew(ArrayMesh);
box_array[RS::ARRAY_COLOR] = colors;
box_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, box_array);
return box_mesh;
}
real_t BoxShape3D::get_enclosing_radius() const {
return size.length() / 2;
}

View file

@ -51,6 +51,7 @@ public:
Vector3 get_size() const;
virtual Vector<Vector3> get_debug_mesh_lines() const override;
virtual Ref<ArrayMesh> get_debug_arraymesh_faces(const Color &p_modulate) const override;
virtual real_t get_enclosing_radius() const override;
BoxShape3D();

View file

@ -30,6 +30,7 @@
#include "capsule_shape_3d.h"
#include "scene/resources/3d/primitive_meshes.h"
#include "servers/physics_server_3d.h"
Vector<Vector3> CapsuleShape3D::get_debug_mesh_lines() const {
@ -67,6 +68,24 @@ Vector<Vector3> CapsuleShape3D::get_debug_mesh_lines() const {
return points;
}
Ref<ArrayMesh> CapsuleShape3D::get_debug_arraymesh_faces(const Color &p_modulate) const {
Array capsule_array;
capsule_array.resize(RS::ARRAY_MAX);
CapsuleMesh::create_mesh_array(capsule_array, radius, height, 32, 8);
Vector<Color> colors;
const PackedVector3Array &verts = capsule_array[RS::ARRAY_VERTEX];
const int32_t verts_size = verts.size();
for (int i = 0; i < verts_size; i++) {
colors.append(p_modulate);
}
Ref<ArrayMesh> capsule_mesh = memnew(ArrayMesh);
capsule_array[RS::ARRAY_COLOR] = colors;
capsule_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, capsule_array);
return capsule_mesh;
}
real_t CapsuleShape3D::get_enclosing_radius() const {
return height * 0.5;
}

View file

@ -33,6 +33,8 @@
#include "scene/resources/3d/shape_3d.h"
class ArrayMesh;
class CapsuleShape3D : public Shape3D {
GDCLASS(CapsuleShape3D, Shape3D);
float radius = 0.5;
@ -50,6 +52,7 @@ public:
float get_height() const;
virtual Vector<Vector3> get_debug_mesh_lines() const override;
virtual Ref<ArrayMesh> get_debug_arraymesh_faces(const Color &p_modulate) const override;
virtual real_t get_enclosing_radius() const override;
CapsuleShape3D();

View file

@ -30,6 +30,7 @@
#include "concave_polygon_shape_3d.h"
#include "scene/resources/mesh.h"
#include "servers/physics_server_3d.h"
Vector<Vector3> ConcavePolygonShape3D::get_debug_mesh_lines() const {
@ -59,6 +60,23 @@ Vector<Vector3> ConcavePolygonShape3D::get_debug_mesh_lines() const {
return points;
}
Ref<ArrayMesh> ConcavePolygonShape3D::get_debug_arraymesh_faces(const Color &p_modulate) const {
Vector<Color> colors;
for (int i = 0; i < faces.size(); i++) {
colors.push_back(p_modulate);
}
Ref<ArrayMesh> mesh = memnew(ArrayMesh);
Array a;
a.resize(Mesh::ARRAY_MAX);
a[RS::ARRAY_VERTEX] = faces;
a[RS::ARRAY_COLOR] = colors;
mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, a);
return mesh;
}
real_t ConcavePolygonShape3D::get_enclosing_radius() const {
Vector<Vector3> data = get_faces();
const Vector3 *read = data.ptr();

View file

@ -33,6 +33,8 @@
#include "scene/resources/3d/shape_3d.h"
class ArrayMesh;
class ConcavePolygonShape3D : public Shape3D {
GDCLASS(ConcavePolygonShape3D, Shape3D);
@ -72,6 +74,7 @@ public:
bool is_backface_collision_enabled() const;
virtual Vector<Vector3> get_debug_mesh_lines() const override;
virtual Ref<ArrayMesh> get_debug_arraymesh_faces(const Color &p_modulate) const override;
virtual real_t get_enclosing_radius() const override;
ConcavePolygonShape3D();

View file

@ -30,12 +30,13 @@
#include "convex_polygon_shape_3d.h"
#include "core/math/convex_hull.h"
#include "scene/resources/mesh.h"
#include "servers/physics_server_3d.h"
Vector<Vector3> ConvexPolygonShape3D::get_debug_mesh_lines() const {
Vector<Vector3> poly_points = get_points();
if (poly_points.size() > 3) {
if (poly_points.size() > 1) { // Need at least 2 points for a line.
Vector<Vector3> varr = Variant(poly_points);
Geometry3D::MeshData md;
Error err = ConvexHullComputer::convex_hull(varr, md);
@ -53,6 +54,44 @@ Vector<Vector3> ConvexPolygonShape3D::get_debug_mesh_lines() const {
return Vector<Vector3>();
}
Ref<ArrayMesh> ConvexPolygonShape3D::get_debug_arraymesh_faces(const Color &p_modulate) const {
const Vector<Vector3> hull_points = get_points();
Vector<Vector3> verts;
Vector<Color> colors;
Vector<int> indices;
if (hull_points.size() >= 3) {
Geometry3D::MeshData md;
Error err = ConvexHullComputer::convex_hull(hull_points, md);
if (err == OK) {
verts = md.vertices;
for (int i = 0; i < verts.size(); i++) {
colors.push_back(p_modulate);
}
for (const Geometry3D::MeshData::Face &face : md.faces) {
const int first_point = face.indices[0];
const int indices_count = face.indices.size();
for (int i = 1; i < indices_count - 1; i++) {
indices.push_back(first_point);
indices.push_back(face.indices[i]);
indices.push_back(face.indices[i + 1]);
}
}
}
}
Ref<ArrayMesh> mesh = memnew(ArrayMesh);
Array a;
a.resize(Mesh::ARRAY_MAX);
a[RS::ARRAY_VERTEX] = verts;
a[RS::ARRAY_COLOR] = colors;
a[RS::ARRAY_INDEX] = indices;
mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, a);
return mesh;
}
real_t ConvexPolygonShape3D::get_enclosing_radius() const {
Vector<Vector3> data = get_points();
const Vector3 *read = data.ptr();

View file

@ -33,6 +33,8 @@
#include "scene/resources/3d/shape_3d.h"
class ArrayMesh;
class ConvexPolygonShape3D : public Shape3D {
GDCLASS(ConvexPolygonShape3D, Shape3D);
Vector<Vector3> points;
@ -47,6 +49,7 @@ public:
Vector<Vector3> get_points() const;
virtual Vector<Vector3> get_debug_mesh_lines() const override;
virtual Ref<ArrayMesh> get_debug_arraymesh_faces(const Color &p_modulate) const override;
virtual real_t get_enclosing_radius() const override;
ConvexPolygonShape3D();

View file

@ -30,6 +30,7 @@
#include "cylinder_shape_3d.h"
#include "scene/resources/3d/primitive_meshes.h"
#include "servers/physics_server_3d.h"
Vector<Vector3> CylinderShape3D::get_debug_mesh_lines() const {
@ -60,6 +61,24 @@ Vector<Vector3> CylinderShape3D::get_debug_mesh_lines() const {
return points;
}
Ref<ArrayMesh> CylinderShape3D::get_debug_arraymesh_faces(const Color &p_modulate) const {
Array cylinder_array;
cylinder_array.resize(RS::ARRAY_MAX);
CylinderMesh::create_mesh_array(cylinder_array, radius, radius, height, 32);
Vector<Color> colors;
const PackedVector3Array &verts = cylinder_array[RS::ARRAY_VERTEX];
const int32_t verts_size = verts.size();
for (int i = 0; i < verts_size; i++) {
colors.append(p_modulate);
}
Ref<ArrayMesh> cylinder_mesh = memnew(ArrayMesh);
cylinder_array[RS::ARRAY_COLOR] = colors;
cylinder_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, cylinder_array);
return cylinder_mesh;
}
real_t CylinderShape3D::get_enclosing_radius() const {
return Vector2(radius, height * 0.5).length();
}

View file

@ -33,6 +33,8 @@
#include "scene/resources/3d/shape_3d.h"
class ArrayMesh;
class CylinderShape3D : public Shape3D {
GDCLASS(CylinderShape3D, Shape3D);
float radius = 0.5;
@ -49,6 +51,7 @@ public:
float get_height() const;
virtual Vector<Vector3> get_debug_mesh_lines() const override;
virtual Ref<ArrayMesh> get_debug_arraymesh_faces(const Color &p_modulate) const override;
virtual real_t get_enclosing_radius() const override;
CylinderShape3D();

View file

@ -138,7 +138,7 @@ void FogMaterial::cleanup_shader() {
}
void FogMaterial::_update_shader() {
shader_mutex.lock();
MutexLock shader_lock(shader_mutex);
if (shader.is_null()) {
shader = RS::get_singleton()->shader_create();
@ -165,10 +165,11 @@ void fog() {
}
)");
}
shader_mutex.unlock();
}
FogMaterial::FogMaterial() {
_set_material(RS::get_singleton()->material_create());
set_density(1.0);
set_albedo(Color(1, 1, 1, 1));
set_emission(Color(0, 0, 0, 1));

View file

@ -31,6 +31,7 @@
#include "height_map_shape_3d.h"
#include "core/io/image.h"
#include "scene/resources/mesh.h"
#include "servers/physics_server_3d.h"
Vector<Vector3> HeightMapShape3D::get_debug_mesh_lines() const {
@ -82,6 +83,60 @@ Vector<Vector3> HeightMapShape3D::get_debug_mesh_lines() const {
return points;
}
Ref<ArrayMesh> HeightMapShape3D::get_debug_arraymesh_faces(const Color &p_modulate) const {
Vector<Vector3> verts;
Vector<Color> colors;
Vector<int> indices;
// This will be slow for large maps...
if ((map_width != 0) && (map_depth != 0)) {
Vector2 size = Vector2(map_width - 1, map_depth - 1) * -0.5;
const real_t *r = map_data.ptr();
for (int d = 0; d <= map_depth - 2; d++) {
const int this_row_offset = map_width * d;
const int next_row_offset = this_row_offset + map_width;
for (int w = 0; w <= map_width - 2; w++) {
const float height_tl = r[next_row_offset + w];
const float height_bl = r[this_row_offset + w];
const float height_br = r[this_row_offset + w + 1];
const float height_tr = r[next_row_offset + w + 1];
const int index_offset = verts.size();
verts.push_back(Vector3(size.x + w, height_tl, size.y + d + 1));
verts.push_back(Vector3(size.x + w, height_bl, size.y + d));
verts.push_back(Vector3(size.x + w + 1, height_br, size.y + d));
verts.push_back(Vector3(size.x + w + 1, height_tr, size.y + d + 1));
colors.push_back(p_modulate);
colors.push_back(p_modulate);
colors.push_back(p_modulate);
colors.push_back(p_modulate);
indices.push_back(index_offset);
indices.push_back(index_offset + 1);
indices.push_back(index_offset + 2);
indices.push_back(index_offset);
indices.push_back(index_offset + 2);
indices.push_back(index_offset + 3);
}
}
}
Ref<ArrayMesh> mesh = memnew(ArrayMesh);
Array a;
a.resize(Mesh::ARRAY_MAX);
a[RS::ARRAY_VERTEX] = verts;
a[RS::ARRAY_COLOR] = colors;
a[RS::ARRAY_INDEX] = indices;
mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, a);
return mesh;
}
real_t HeightMapShape3D::get_enclosing_radius() const {
return Vector3(real_t(map_width), max_height - min_height, real_t(map_depth)).length();
}
@ -298,8 +353,8 @@ void HeightMapShape3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("update_map_data_from_image", "image", "height_min", "height_max"), &HeightMapShape3D::update_map_data_from_image);
ADD_PROPERTY(PropertyInfo(Variant::INT, "map_width", PROPERTY_HINT_RANGE, "0.001,100,0.001,or_greater"), "set_map_width", "get_map_width");
ADD_PROPERTY(PropertyInfo(Variant::INT, "map_depth", PROPERTY_HINT_RANGE, "0.001,100,0.001,or_greater"), "set_map_depth", "get_map_depth");
ADD_PROPERTY(PropertyInfo(Variant::INT, "map_width", PROPERTY_HINT_RANGE, "1,100,1,or_greater"), "set_map_width", "get_map_width");
ADD_PROPERTY(PropertyInfo(Variant::INT, "map_depth", PROPERTY_HINT_RANGE, "1,100,1,or_greater"), "set_map_depth", "get_map_depth");
ADD_PROPERTY(PropertyInfo(Variant::PACKED_FLOAT32_ARRAY, "map_data"), "set_map_data", "get_map_data");
}

View file

@ -33,6 +33,7 @@
#include "scene/resources/3d/shape_3d.h"
class ArrayMesh;
class Image;
class HeightMapShape3D : public Shape3D {
@ -62,6 +63,7 @@ public:
void update_map_data_from_image(const Ref<Image> &p_image, real_t p_height_min, real_t p_height_max);
virtual Vector<Vector3> get_debug_mesh_lines() const override;
virtual Ref<ArrayMesh> get_debug_arraymesh_faces(const Color &p_modulate) const override;
virtual real_t get_enclosing_radius() const override;
HeightMapShape3D();

View file

@ -33,110 +33,22 @@
#include "core/io/marshalls.h"
#include "core/math/convex_hull.h"
#include "core/math/random_pcg.h"
#include "core/math/static_raycaster.h"
#include "scene/resources/surface_tool.h"
#include <cstdint>
void ImporterMesh::Surface::split_normals(const LocalVector<int> &p_indices, const LocalVector<Vector3> &p_normals) {
_split_normals(arrays, p_indices, p_normals);
for (BlendShape &blend_shape : blend_shape_data) {
_split_normals(blend_shape.arrays, p_indices, p_normals);
}
}
void ImporterMesh::Surface::_split_normals(Array &r_arrays, const LocalVector<int> &p_indices, const LocalVector<Vector3> &p_normals) {
ERR_FAIL_COND(r_arrays.size() != RS::ARRAY_MAX);
const PackedVector3Array &vertices = r_arrays[RS::ARRAY_VERTEX];
int current_vertex_count = vertices.size();
int new_vertex_count = p_indices.size();
int final_vertex_count = current_vertex_count + new_vertex_count;
const int *indices_ptr = p_indices.ptr();
for (int i = 0; i < r_arrays.size(); i++) {
if (i == RS::ARRAY_INDEX) {
continue;
}
if (r_arrays[i].get_type() == Variant::NIL) {
continue;
}
switch (r_arrays[i].get_type()) {
case Variant::PACKED_VECTOR3_ARRAY: {
PackedVector3Array data = r_arrays[i];
data.resize(final_vertex_count);
Vector3 *data_ptr = data.ptrw();
if (i == RS::ARRAY_NORMAL) {
const Vector3 *normals_ptr = p_normals.ptr();
memcpy(&data_ptr[current_vertex_count], normals_ptr, sizeof(Vector3) * new_vertex_count);
} else {
for (int j = 0; j < new_vertex_count; j++) {
data_ptr[current_vertex_count + j] = data_ptr[indices_ptr[j]];
}
}
r_arrays[i] = data;
} break;
case Variant::PACKED_VECTOR2_ARRAY: {
PackedVector2Array data = r_arrays[i];
data.resize(final_vertex_count);
Vector2 *data_ptr = data.ptrw();
for (int j = 0; j < new_vertex_count; j++) {
data_ptr[current_vertex_count + j] = data_ptr[indices_ptr[j]];
}
r_arrays[i] = data;
} break;
case Variant::PACKED_FLOAT32_ARRAY: {
PackedFloat32Array data = r_arrays[i];
int elements = data.size() / current_vertex_count;
data.resize(final_vertex_count * elements);
float *data_ptr = data.ptrw();
for (int j = 0; j < new_vertex_count; j++) {
memcpy(&data_ptr[(current_vertex_count + j) * elements], &data_ptr[indices_ptr[j] * elements], sizeof(float) * elements);
}
r_arrays[i] = data;
} break;
case Variant::PACKED_INT32_ARRAY: {
PackedInt32Array data = r_arrays[i];
int elements = data.size() / current_vertex_count;
data.resize(final_vertex_count * elements);
int32_t *data_ptr = data.ptrw();
for (int j = 0; j < new_vertex_count; j++) {
memcpy(&data_ptr[(current_vertex_count + j) * elements], &data_ptr[indices_ptr[j] * elements], sizeof(int32_t) * elements);
}
r_arrays[i] = data;
} break;
case Variant::PACKED_BYTE_ARRAY: {
PackedByteArray data = r_arrays[i];
int elements = data.size() / current_vertex_count;
data.resize(final_vertex_count * elements);
uint8_t *data_ptr = data.ptrw();
for (int j = 0; j < new_vertex_count; j++) {
memcpy(&data_ptr[(current_vertex_count + j) * elements], &data_ptr[indices_ptr[j] * elements], sizeof(uint8_t) * elements);
}
r_arrays[i] = data;
} break;
case Variant::PACKED_COLOR_ARRAY: {
PackedColorArray data = r_arrays[i];
data.resize(final_vertex_count);
Color *data_ptr = data.ptrw();
for (int j = 0; j < new_vertex_count; j++) {
data_ptr[current_vertex_count + j] = data_ptr[indices_ptr[j]];
}
r_arrays[i] = data;
} break;
default: {
ERR_FAIL_MSG("Unhandled array type.");
} break;
}
String ImporterMesh::validate_blend_shape_name(const String &p_name) {
String name = p_name;
const char *characters = ":";
for (const char *p = characters; *p; p++) {
name = name.replace(String::chr(*p), "_");
}
return name;
}
void ImporterMesh::add_blend_shape(const String &p_name) {
ERR_FAIL_COND(surfaces.size() > 0);
blend_shapes.push_back(p_name);
blend_shapes.push_back(validate_blend_shape_name(p_name));
}
int ImporterMesh::get_blend_shape_count() const {
@ -256,6 +168,117 @@ void ImporterMesh::set_surface_material(int p_surface, const Ref<Material> &p_ma
mesh.unref();
}
template <typename T>
static Vector<T> _remap_array(Vector<T> p_array, const Vector<uint32_t> &p_remap, uint32_t p_vertex_count) {
ERR_FAIL_COND_V(p_array.size() % p_remap.size() != 0, p_array);
int num_elements = p_array.size() / p_remap.size();
T *data = p_array.ptrw();
SurfaceTool::remap_vertex_func(data, data, p_remap.size(), sizeof(T) * num_elements, p_remap.ptr());
p_array.resize(p_vertex_count * num_elements);
return p_array;
}
static void _remap_arrays(Array &r_arrays, const Vector<uint32_t> &p_remap, uint32_t p_vertex_count) {
for (int i = 0; i < r_arrays.size(); i++) {
if (i == RS::ARRAY_INDEX) {
continue;
}
switch (r_arrays[i].get_type()) {
case Variant::NIL:
break;
case Variant::PACKED_VECTOR3_ARRAY:
r_arrays[i] = _remap_array<Vector3>(r_arrays[i], p_remap, p_vertex_count);
break;
case Variant::PACKED_VECTOR2_ARRAY:
r_arrays[i] = _remap_array<Vector2>(r_arrays[i], p_remap, p_vertex_count);
break;
case Variant::PACKED_FLOAT32_ARRAY:
r_arrays[i] = _remap_array<float>(r_arrays[i], p_remap, p_vertex_count);
break;
case Variant::PACKED_INT32_ARRAY:
r_arrays[i] = _remap_array<int32_t>(r_arrays[i], p_remap, p_vertex_count);
break;
case Variant::PACKED_BYTE_ARRAY:
r_arrays[i] = _remap_array<uint8_t>(r_arrays[i], p_remap, p_vertex_count);
break;
case Variant::PACKED_COLOR_ARRAY:
r_arrays[i] = _remap_array<Color>(r_arrays[i], p_remap, p_vertex_count);
break;
default:
ERR_FAIL_MSG("Unhandled array type.");
}
}
}
void ImporterMesh::optimize_indices() {
if (!SurfaceTool::optimize_vertex_cache_func) {
return;
}
if (!SurfaceTool::optimize_vertex_fetch_remap_func || !SurfaceTool::remap_vertex_func || !SurfaceTool::remap_index_func) {
return;
}
for (int i = 0; i < surfaces.size(); i++) {
if (surfaces[i].primitive != Mesh::PRIMITIVE_TRIANGLES) {
continue;
}
Vector<Vector3> vertices = surfaces[i].arrays[RS::ARRAY_VERTEX];
PackedInt32Array indices = surfaces[i].arrays[RS::ARRAY_INDEX];
unsigned int index_count = indices.size();
unsigned int vertex_count = vertices.size();
if (index_count == 0) {
continue;
}
// Optimize indices for vertex cache to establish final triangle order.
int *indices_ptr = indices.ptrw();
SurfaceTool::optimize_vertex_cache_func((unsigned int *)indices_ptr, (const unsigned int *)indices_ptr, index_count, vertex_count);
surfaces.write[i].arrays[RS::ARRAY_INDEX] = indices;
for (int j = 0; j < surfaces[i].lods.size(); ++j) {
Surface::LOD &lod = surfaces.write[i].lods.write[j];
int *lod_indices_ptr = lod.indices.ptrw();
SurfaceTool::optimize_vertex_cache_func((unsigned int *)lod_indices_ptr, (const unsigned int *)lod_indices_ptr, lod.indices.size(), vertex_count);
}
// Concatenate indices for all LODs in the order of coarse->fine; this establishes the effective order of vertices,
// and is important to optimize for vertex fetch (all GPUs) and shading (Mali GPUs)
PackedInt32Array merged_indices;
for (int j = surfaces[i].lods.size() - 1; j >= 0; --j) {
merged_indices.append_array(surfaces[i].lods[j].indices);
}
merged_indices.append_array(indices);
// Generate remap array that establishes optimal vertex order according to the order of indices above.
Vector<uint32_t> remap;
remap.resize(vertex_count);
unsigned int new_vertex_count = SurfaceTool::optimize_vertex_fetch_remap_func(remap.ptrw(), (const unsigned int *)merged_indices.ptr(), merged_indices.size(), vertex_count);
// We need to remap all vertex and index arrays in lockstep according to the remap.
SurfaceTool::remap_index_func((unsigned int *)indices_ptr, (const unsigned int *)indices_ptr, index_count, remap.ptr());
surfaces.write[i].arrays[RS::ARRAY_INDEX] = indices;
for (int j = 0; j < surfaces[i].lods.size(); ++j) {
Surface::LOD &lod = surfaces.write[i].lods.write[j];
int *lod_indices_ptr = lod.indices.ptrw();
SurfaceTool::remap_index_func((unsigned int *)lod_indices_ptr, (const unsigned int *)lod_indices_ptr, lod.indices.size(), remap.ptr());
}
_remap_arrays(surfaces.write[i].arrays, remap, new_vertex_count);
for (int j = 0; j < surfaces[i].blend_shape_data.size(); j++) {
_remap_arrays(surfaces.write[i].blend_shape_data.write[j].arrays, remap, new_vertex_count);
}
}
if (shadow_mesh.is_valid()) {
shadow_mesh->optimize_indices();
}
}
#define VERTEX_SKIN_FUNC(bone_count, vert_idx, read_array, write_array, transform_array, bone_array, weight_array) \
Vector3 transformed_vert; \
for (unsigned int weight_idx = 0; weight_idx < bone_count; weight_idx++) { \
@ -269,16 +292,13 @@ void ImporterMesh::set_surface_material(int p_surface, const Ref<Material> &p_ma
} \
write_array[vert_idx] = transformed_vert;
void ImporterMesh::generate_lods(float p_normal_merge_angle, float p_normal_split_angle, Array p_bone_transform_array) {
void ImporterMesh::generate_lods(float p_normal_merge_angle, Array p_bone_transform_array) {
if (!SurfaceTool::simplify_scale_func) {
return;
}
if (!SurfaceTool::simplify_with_attrib_func) {
return;
}
if (!SurfaceTool::optimize_vertex_cache_func) {
return;
}
LocalVector<Transform3D> bone_transform_vector;
for (int i = 0; i < p_bone_transform_array.size(); i++) {
@ -342,8 +362,6 @@ void ImporterMesh::generate_lods(float p_normal_merge_angle, float p_normal_spli
}
float normal_merge_threshold = Math::cos(Math::deg_to_rad(p_normal_merge_angle));
float normal_pre_split_threshold = Math::cos(Math::deg_to_rad(MIN(180.0f, p_normal_split_angle * 2.0f)));
float normal_split_threshold = Math::cos(Math::deg_to_rad(p_normal_split_angle));
const Vector3 *normals_ptr = normals.ptr();
HashMap<Vector3, LocalVector<Pair<int, int>>> unique_vertices;
@ -432,21 +450,6 @@ void ImporterMesh::generate_lods(float p_normal_merge_angle, float p_normal_spli
unsigned int index_target = 12; // Start with the smallest target, 4 triangles
unsigned int last_index_count = 0;
int split_vertex_count = vertex_count;
LocalVector<Vector3> split_vertex_normals;
LocalVector<int> split_vertex_indices;
split_vertex_normals.reserve(index_count / 3);
split_vertex_indices.reserve(index_count / 3);
RandomPCG pcg;
pcg.seed(123456789); // Keep seed constant across imports
Ref<StaticRaycaster> raycaster = StaticRaycaster::create();
if (raycaster.is_valid()) {
raycaster->add_mesh(vertices, indices, 0);
raycaster->commit();
}
const float max_mesh_error = FLT_MAX; // We don't want to limit by error, just by index target
float mesh_error = 0.0f;
@ -465,6 +468,7 @@ void ImporterMesh::generate_lods(float p_normal_merge_angle, float p_normal_spli
merged_normals_f32.ptr(),
sizeof(float) * 3, // Attribute stride
normal_weights, 3,
nullptr, // Vertex lock
index_target,
max_mesh_error,
simplify_options,
@ -488,174 +492,10 @@ void ImporterMesh::generate_lods(float p_normal_merge_angle, float p_normal_spli
}
new_indices.resize(new_index_count);
LocalVector<LocalVector<int>> vertex_corners;
vertex_corners.resize(vertex_count);
{
int *ptrw = new_indices.ptrw();
for (unsigned int j = 0; j < new_index_count; j++) {
const int &remapped = vertex_inverse_remap[ptrw[j]];
vertex_corners[remapped].push_back(j);
ptrw[j] = remapped;
}
}
if (raycaster.is_valid()) {
float error_factor = 1.0f / (scale * MAX(mesh_error, 0.15));
const float ray_bias = 0.05;
float ray_length = ray_bias + mesh_error * scale * 3.0f;
Vector<StaticRaycaster::Ray> rays;
LocalVector<Vector2> ray_uvs;
int32_t *new_indices_ptr = new_indices.ptrw();
int current_ray_count = 0;
for (unsigned int j = 0; j < new_index_count; j += 3) {
const Vector3 &v0 = vertices_ptr[new_indices_ptr[j + 0]];
const Vector3 &v1 = vertices_ptr[new_indices_ptr[j + 1]];
const Vector3 &v2 = vertices_ptr[new_indices_ptr[j + 2]];
Vector3 face_normal = vec3_cross(v0 - v2, v0 - v1);
float face_area = face_normal.length(); // Actually twice the face area, since it's the same error_factor on all faces, we don't care
if (!Math::is_finite(face_area) || face_area == 0) {
WARN_PRINT_ONCE("Ignoring face with non-finite normal in LOD generation.");
continue;
}
Vector3 dir = face_normal / face_area;
int ray_count = CLAMP(5.0 * face_area * error_factor, 16, 64);
rays.resize(current_ray_count + ray_count);
StaticRaycaster::Ray *rays_ptr = rays.ptrw();
ray_uvs.resize(current_ray_count + ray_count);
Vector2 *ray_uvs_ptr = ray_uvs.ptr();
for (int k = 0; k < ray_count; k++) {
float u = pcg.randf();
float v = pcg.randf();
if (u + v >= 1.0f) {
u = 1.0f - u;
v = 1.0f - v;
}
u = 0.9f * u + 0.05f / 3.0f; // Give barycentric coordinates some padding, we don't want to sample right on the edge
v = 0.9f * v + 0.05f / 3.0f; // v = (v - one_third) * 0.95f + one_third;
float w = 1.0f - u - v;
Vector3 org = v0 * w + v1 * u + v2 * v;
org -= dir * ray_bias;
rays_ptr[current_ray_count + k] = StaticRaycaster::Ray(org, dir, 0.0f, ray_length);
rays_ptr[current_ray_count + k].id = j / 3;
ray_uvs_ptr[current_ray_count + k] = Vector2(u, v);
}
current_ray_count += ray_count;
}
raycaster->intersect(rays);
LocalVector<Vector3> ray_normals;
LocalVector<real_t> ray_normal_weights;
ray_normals.resize(new_index_count);
ray_normal_weights.resize(new_index_count);
for (unsigned int j = 0; j < new_index_count; j++) {
ray_normal_weights[j] = 0.0f;
}
const StaticRaycaster::Ray *rp = rays.ptr();
for (int j = 0; j < rays.size(); j++) {
if (rp[j].geomID != 0) { // Ray missed
continue;
}
if (rp[j].normal.normalized().dot(rp[j].dir) > 0.0f) { // Hit a back face.
continue;
}
const float &u = rp[j].u;
const float &v = rp[j].v;
const float w = 1.0f - u - v;
const unsigned int &hit_tri_id = rp[j].primID;
const unsigned int &orig_tri_id = rp[j].id;
const Vector3 &n0 = normals_ptr[indices_ptr[hit_tri_id * 3 + 0]];
const Vector3 &n1 = normals_ptr[indices_ptr[hit_tri_id * 3 + 1]];
const Vector3 &n2 = normals_ptr[indices_ptr[hit_tri_id * 3 + 2]];
Vector3 normal = n0 * w + n1 * u + n2 * v;
Vector2 orig_uv = ray_uvs[j];
const real_t orig_bary[3] = { 1.0f - orig_uv.x - orig_uv.y, orig_uv.x, orig_uv.y };
for (int k = 0; k < 3; k++) {
int idx = orig_tri_id * 3 + k;
real_t weight = orig_bary[k];
ray_normals[idx] += normal * weight;
ray_normal_weights[idx] += weight;
}
}
for (unsigned int j = 0; j < new_index_count; j++) {
if (ray_normal_weights[j] < 1.0f) { // Not enough data, the new normal would be just a bad guess
ray_normals[j] = Vector3();
} else {
ray_normals[j] /= ray_normal_weights[j];
}
}
LocalVector<LocalVector<int>> normal_group_indices;
LocalVector<Vector3> normal_group_averages;
normal_group_indices.reserve(24);
normal_group_averages.reserve(24);
for (unsigned int j = 0; j < vertex_count; j++) {
const LocalVector<int> &corners = vertex_corners[j];
const Vector3 &vertex_normal = normals_ptr[j];
for (const int &corner_idx : corners) {
const Vector3 &ray_normal = ray_normals[corner_idx];
if (ray_normal.length_squared() < CMP_EPSILON2) {
continue;
}
bool found = false;
for (unsigned int l = 0; l < normal_group_indices.size(); l++) {
LocalVector<int> &group_indices = normal_group_indices[l];
Vector3 n = normal_group_averages[l] / group_indices.size();
if (n.dot(ray_normal) > normal_pre_split_threshold) {
found = true;
group_indices.push_back(corner_idx);
normal_group_averages[l] += ray_normal;
break;
}
}
if (!found) {
normal_group_indices.push_back({ corner_idx });
normal_group_averages.push_back(ray_normal);
}
}
for (unsigned int k = 0; k < normal_group_indices.size(); k++) {
LocalVector<int> &group_indices = normal_group_indices[k];
Vector3 n = normal_group_averages[k] / group_indices.size();
if (vertex_normal.dot(n) < normal_split_threshold) {
split_vertex_indices.push_back(j);
split_vertex_normals.push_back(n);
int new_idx = split_vertex_count++;
for (const int &index : group_indices) {
new_indices_ptr[index] = new_idx;
}
}
}
normal_group_indices.clear();
normal_group_averages.clear();
ptrw[j] = vertex_inverse_remap[ptrw[j]];
}
}
@ -671,17 +511,15 @@ void ImporterMesh::generate_lods(float p_normal_merge_angle, float p_normal_spli
}
}
surfaces.write[i].split_normals(split_vertex_indices, split_vertex_normals);
surfaces.write[i].lods.sort_custom<Surface::LODComparator>();
for (int j = 0; j < surfaces.write[i].lods.size(); j++) {
Surface::LOD &lod = surfaces.write[i].lods.write[j];
unsigned int *lod_indices_ptr = (unsigned int *)lod.indices.ptrw();
SurfaceTool::optimize_vertex_cache_func(lod_indices_ptr, lod_indices_ptr, lod.indices.size(), split_vertex_count);
}
}
}
void ImporterMesh::_generate_lods_bind(float p_normal_merge_angle, float p_normal_split_angle, Array p_skin_pose_transform_array) {
// p_normal_split_angle is unused, but kept for compatibility
generate_lods(p_normal_merge_angle, p_skin_pose_transform_array);
}
bool ImporterMesh::has_mesh() const {
return mesh.is_valid();
}
@ -1062,9 +900,12 @@ Ref<NavigationMesh> ImporterMesh::create_navigation_mesh() {
}
HashMap<Vector3, int> unique_vertices;
LocalVector<int> face_indices;
Vector<Vector<int>> face_polygons;
face_polygons.resize(faces.size());
for (int i = 0; i < faces.size(); i++) {
Vector<int> face_indices;
face_indices.resize(3);
for (int j = 0; j < 3; j++) {
Vector3 v = faces[i].vertex[j];
int idx;
@ -1074,8 +915,9 @@ Ref<NavigationMesh> ImporterMesh::create_navigation_mesh() {
idx = unique_vertices.size();
unique_vertices[v] = idx;
}
face_indices.push_back(idx);
face_indices.write[j] = idx;
}
face_polygons.write[i] = face_indices;
}
Vector<Vector3> vertices;
@ -1086,16 +928,7 @@ Ref<NavigationMesh> ImporterMesh::create_navigation_mesh() {
Ref<NavigationMesh> nm;
nm.instantiate();
nm->set_vertices(vertices);
Vector<int> v3;
v3.resize(3);
for (uint32_t i = 0; i < face_indices.size(); i += 3) {
v3.write[0] = face_indices[i + 0];
v3.write[1] = face_indices[i + 1];
v3.write[2] = face_indices[i + 2];
nm->add_polygon(v3);
}
nm->set_data(vertices, face_polygons);
return nm;
}
@ -1367,7 +1200,7 @@ void ImporterMesh::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_surface_name", "surface_idx", "name"), &ImporterMesh::set_surface_name);
ClassDB::bind_method(D_METHOD("set_surface_material", "surface_idx", "material"), &ImporterMesh::set_surface_material);
ClassDB::bind_method(D_METHOD("generate_lods", "normal_merge_angle", "normal_split_angle", "bone_transform_array"), &ImporterMesh::generate_lods);
ClassDB::bind_method(D_METHOD("generate_lods", "normal_merge_angle", "normal_split_angle", "bone_transform_array"), &ImporterMesh::_generate_lods_bind);
ClassDB::bind_method(D_METHOD("get_mesh", "base_mesh"), &ImporterMesh::get_mesh, DEFVAL(Ref<ArrayMesh>()));
ClassDB::bind_method(D_METHOD("clear"), &ImporterMesh::clear);
@ -1377,5 +1210,5 @@ void ImporterMesh::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_lightmap_size_hint", "size"), &ImporterMesh::set_lightmap_size_hint);
ClassDB::bind_method(D_METHOD("get_lightmap_size_hint"), &ImporterMesh::get_lightmap_size_hint);
ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "_set_data", "_get_data");
ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data");
}

View file

@ -32,7 +32,6 @@
#define IMPORTER_MESH_H
#include "core/io/resource.h"
#include "core/templates/local_vector.h"
#include "scene/resources/3d/concave_polygon_shape_3d.h"
#include "scene/resources/3d/convex_polygon_shape_3d.h"
#include "scene/resources/mesh.h"
@ -68,9 +67,6 @@ class ImporterMesh : public Resource {
return l.distance < r.distance;
}
};
void split_normals(const LocalVector<int> &p_indices, const LocalVector<Vector3> &p_normals);
static void _split_normals(Array &r_arrays, const LocalVector<int> &p_indices, const LocalVector<Vector3> &p_normals);
};
Vector<Surface> surfaces;
Vector<String> blend_shapes;
@ -86,6 +82,8 @@ protected:
void _set_data(const Dictionary &p_data);
Dictionary _get_data() const;
void _generate_lods_bind(float p_normal_merge_angle, float p_normal_split_angle, Array p_skin_pose_transform_array);
static void _bind_methods();
public:
@ -93,6 +91,8 @@ public:
int get_blend_shape_count() const;
String get_blend_shape_name(int p_blend_shape) const;
static String validate_blend_shape_name(const String &p_name);
void add_surface(Mesh::PrimitiveType p_primitive, const Array &p_arrays, const TypedArray<Array> &p_blend_shapes = Array(), const Dictionary &p_lods = Dictionary(), const Ref<Material> &p_material = Ref<Material>(), const String &p_name = String(), const uint64_t p_flags = 0);
int get_surface_count() const;
@ -112,7 +112,9 @@ public:
void set_surface_material(int p_surface, const Ref<Material> &p_material);
void generate_lods(float p_normal_merge_angle, float p_normal_split_angle, Array p_skin_pose_transform_array);
void optimize_indices();
void generate_lods(float p_normal_merge_angle, Array p_skin_pose_transform_array);
void create_shadow_mesh();
Ref<ImporterMesh> get_shadow_mesh() const;

View file

@ -47,6 +47,24 @@ bool MeshLibrary::_set(const StringName &p_name, const Variant &p_value) {
set_item_mesh(idx, p_value);
} else if (what == "mesh_transform") {
set_item_mesh_transform(idx, p_value);
} else if (what == "mesh_cast_shadow") {
switch ((int)p_value) {
case 0: {
set_item_mesh_cast_shadow(idx, RS::ShadowCastingSetting::SHADOW_CASTING_SETTING_OFF);
} break;
case 1: {
set_item_mesh_cast_shadow(idx, RS::ShadowCastingSetting::SHADOW_CASTING_SETTING_ON);
} break;
case 2: {
set_item_mesh_cast_shadow(idx, RS::ShadowCastingSetting::SHADOW_CASTING_SETTING_DOUBLE_SIDED);
} break;
case 3: {
set_item_mesh_cast_shadow(idx, RS::ShadowCastingSetting::SHADOW_CASTING_SETTING_SHADOWS_ONLY);
} break;
default: {
set_item_mesh_cast_shadow(idx, RS::ShadowCastingSetting::SHADOW_CASTING_SETTING_ON);
} break;
}
} else if (what == "shape") {
Vector<ShapeData> shapes;
ShapeData sd;
@ -91,6 +109,8 @@ bool MeshLibrary::_get(const StringName &p_name, Variant &r_ret) const {
r_ret = get_item_mesh(idx);
} else if (what == "mesh_transform") {
r_ret = get_item_mesh_transform(idx);
} else if (what == "mesh_cast_shadow") {
r_ret = (int)get_item_mesh_cast_shadow(idx);
} else if (what == "shapes") {
r_ret = _get_item_shapes(idx);
} else if (what == "navigation_mesh") {
@ -120,6 +140,7 @@ void MeshLibrary::_get_property_list(List<PropertyInfo> *p_list) const {
p_list->push_back(PropertyInfo(Variant::STRING, prop_name + PNAME("name")));
p_list->push_back(PropertyInfo(Variant::OBJECT, prop_name + PNAME("mesh"), PROPERTY_HINT_RESOURCE_TYPE, "Mesh"));
p_list->push_back(PropertyInfo(Variant::TRANSFORM3D, prop_name + PNAME("mesh_transform"), PROPERTY_HINT_NONE, "suffix:m"));
p_list->push_back(PropertyInfo(Variant::INT, prop_name + PNAME("mesh_cast_shadow"), PROPERTY_HINT_ENUM, "Off,On,Double-Sided,Shadows Only"));
p_list->push_back(PropertyInfo(Variant::ARRAY, prop_name + PNAME("shapes")));
p_list->push_back(PropertyInfo(Variant::OBJECT, prop_name + PNAME("navigation_mesh"), PROPERTY_HINT_RESOURCE_TYPE, "NavigationMesh"));
p_list->push_back(PropertyInfo(Variant::TRANSFORM3D, prop_name + PNAME("navigation_mesh_transform"), PROPERTY_HINT_NONE, "suffix:m"));
@ -154,6 +175,12 @@ void MeshLibrary::set_item_mesh_transform(int p_item, const Transform3D &p_trans
emit_changed();
}
void MeshLibrary::set_item_mesh_cast_shadow(int p_item, RS::ShadowCastingSetting p_shadow_casting_setting) {
ERR_FAIL_COND_MSG(!item_map.has(p_item), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'.");
item_map[p_item].mesh_cast_shadow = p_shadow_casting_setting;
emit_changed();
}
void MeshLibrary::set_item_shapes(int p_item, const Vector<ShapeData> &p_shapes) {
ERR_FAIL_COND_MSG(!item_map.has(p_item), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'.");
item_map[p_item].shapes = p_shapes;
@ -200,6 +227,11 @@ Transform3D MeshLibrary::get_item_mesh_transform(int p_item) const {
return item_map[p_item].mesh_transform;
}
RS::ShadowCastingSetting MeshLibrary::get_item_mesh_cast_shadow(int p_item) const {
ERR_FAIL_COND_V_MSG(!item_map.has(p_item), RS::ShadowCastingSetting::SHADOW_CASTING_SETTING_ON, "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'.");
return item_map[p_item].mesh_cast_shadow;
}
Vector<MeshLibrary::ShapeData> MeshLibrary::get_item_shapes(int p_item) const {
ERR_FAIL_COND_V_MSG(!item_map.has(p_item), Vector<ShapeData>(), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'.");
return item_map[p_item].shapes;
@ -328,6 +360,7 @@ void MeshLibrary::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_item_name", "id", "name"), &MeshLibrary::set_item_name);
ClassDB::bind_method(D_METHOD("set_item_mesh", "id", "mesh"), &MeshLibrary::set_item_mesh);
ClassDB::bind_method(D_METHOD("set_item_mesh_transform", "id", "mesh_transform"), &MeshLibrary::set_item_mesh_transform);
ClassDB::bind_method(D_METHOD("set_item_mesh_cast_shadow", "id", "shadow_casting_setting"), &MeshLibrary::set_item_mesh_cast_shadow);
ClassDB::bind_method(D_METHOD("set_item_navigation_mesh", "id", "navigation_mesh"), &MeshLibrary::set_item_navigation_mesh);
ClassDB::bind_method(D_METHOD("set_item_navigation_mesh_transform", "id", "navigation_mesh"), &MeshLibrary::set_item_navigation_mesh_transform);
ClassDB::bind_method(D_METHOD("set_item_navigation_layers", "id", "navigation_layers"), &MeshLibrary::set_item_navigation_layers);
@ -336,6 +369,7 @@ void MeshLibrary::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_item_name", "id"), &MeshLibrary::get_item_name);
ClassDB::bind_method(D_METHOD("get_item_mesh", "id"), &MeshLibrary::get_item_mesh);
ClassDB::bind_method(D_METHOD("get_item_mesh_transform", "id"), &MeshLibrary::get_item_mesh_transform);
ClassDB::bind_method(D_METHOD("get_item_mesh_cast_shadow", "id"), &MeshLibrary::get_item_mesh_cast_shadow);
ClassDB::bind_method(D_METHOD("get_item_navigation_mesh", "id"), &MeshLibrary::get_item_navigation_mesh);
ClassDB::bind_method(D_METHOD("get_item_navigation_mesh_transform", "id"), &MeshLibrary::get_item_navigation_mesh_transform);
ClassDB::bind_method(D_METHOD("get_item_navigation_layers", "id"), &MeshLibrary::get_item_navigation_layers);

View file

@ -33,8 +33,9 @@
#include "core/io/resource.h"
#include "core/templates/rb_map.h"
#include "scene/3d/navigation_region_3d.h"
#include "scene/resources/mesh.h"
#include "scene/resources/navigation_mesh.h"
#include "servers/rendering_server.h"
#include "shape_3d.h"
class MeshLibrary : public Resource {
@ -50,6 +51,7 @@ public:
String name;
Ref<Mesh> mesh;
Transform3D mesh_transform;
RS::ShadowCastingSetting mesh_cast_shadow = RS::ShadowCastingSetting::SHADOW_CASTING_SETTING_ON;
Vector<ShapeData> shapes;
Ref<Texture2D> preview;
Ref<NavigationMesh> navigation_mesh;
@ -75,6 +77,7 @@ public:
void set_item_name(int p_item, const String &p_name);
void set_item_mesh(int p_item, const Ref<Mesh> &p_mesh);
void set_item_mesh_transform(int p_item, const Transform3D &p_transform);
void set_item_mesh_cast_shadow(int p_item, RS::ShadowCastingSetting p_shadow_casting_setting);
void set_item_navigation_mesh(int p_item, const Ref<NavigationMesh> &p_navigation_mesh);
void set_item_navigation_mesh_transform(int p_item, const Transform3D &p_transform);
void set_item_navigation_layers(int p_item, uint32_t p_navigation_layers);
@ -83,6 +86,7 @@ public:
String get_item_name(int p_item) const;
Ref<Mesh> get_item_mesh(int p_item) const;
Transform3D get_item_mesh_transform(int p_item) const;
RS::ShadowCastingSetting get_item_mesh_cast_shadow(int p_item) const;
Ref<NavigationMesh> get_item_navigation_mesh(int p_item) const;
Transform3D get_item_navigation_mesh_transform(int p_item) const;
uint32_t get_item_navigation_layers(int p_item) const;

View file

@ -33,6 +33,7 @@
void NavigationMeshSourceGeometryData3D::set_vertices(const Vector<float> &p_vertices) {
RWLockWrite write_lock(geometry_rwlock);
vertices = p_vertices;
bounds_dirty = true;
}
const Vector<float> &NavigationMeshSourceGeometryData3D::get_vertices() const {
@ -44,6 +45,7 @@ void NavigationMeshSourceGeometryData3D::set_indices(const Vector<int> &p_indice
ERR_FAIL_COND(vertices.size() < p_indices.size());
RWLockWrite write_lock(geometry_rwlock);
indices = p_indices;
bounds_dirty = true;
}
const Vector<int> &NavigationMeshSourceGeometryData3D::get_indices() const {
@ -63,23 +65,26 @@ void NavigationMeshSourceGeometryData3D::append_arrays(const Vector<float> &p_ve
for (int64_t i = number_of_indices_before_merge; i < indices.size(); i++) {
indices.set(i, indices[i] + number_of_vertices_before_merge / 3);
}
bounds_dirty = true;
}
bool NavigationMeshSourceGeometryData3D::has_data() {
RWLockRead read_lock(geometry_rwlock);
return vertices.size() && indices.size();
};
}
void NavigationMeshSourceGeometryData3D::clear() {
RWLockWrite write_lock(geometry_rwlock);
vertices.clear();
indices.clear();
_projected_obstructions.clear();
bounds_dirty = true;
}
void NavigationMeshSourceGeometryData3D::clear_projected_obstructions() {
RWLockWrite write_lock(geometry_rwlock);
_projected_obstructions.clear();
bounds_dirty = true;
}
void NavigationMeshSourceGeometryData3D::_add_vertex(const Vector3 &p_vec3) {
@ -190,7 +195,7 @@ void NavigationMeshSourceGeometryData3D::_add_faces(const PackedVector3Array &p_
}
void NavigationMeshSourceGeometryData3D::add_mesh(const Ref<Mesh> &p_mesh, const Transform3D &p_xform) {
ERR_FAIL_COND(!p_mesh.is_valid());
ERR_FAIL_COND(p_mesh.is_null());
#ifdef DEBUG_ENABLED
if (!Engine::get_singleton()->is_editor_hint()) {
@ -207,16 +212,18 @@ void NavigationMeshSourceGeometryData3D::add_mesh_array(const Array &p_mesh_arra
ERR_FAIL_COND(p_mesh_array.size() != Mesh::ARRAY_MAX);
RWLockWrite write_lock(geometry_rwlock);
_add_mesh_array(p_mesh_array, root_node_transform * p_xform);
bounds_dirty = true;
}
void NavigationMeshSourceGeometryData3D::add_faces(const PackedVector3Array &p_faces, const Transform3D &p_xform) {
ERR_FAIL_COND(p_faces.size() % 3 != 0);
RWLockWrite write_lock(geometry_rwlock);
_add_faces(p_faces, root_node_transform * p_xform);
bounds_dirty = true;
}
void NavigationMeshSourceGeometryData3D::merge(const Ref<NavigationMeshSourceGeometryData3D> &p_other_geometry) {
ERR_FAIL_NULL(p_other_geometry);
ERR_FAIL_COND(p_other_geometry.is_null());
Vector<float> other_vertices;
Vector<int> other_indices;
@ -236,6 +243,7 @@ void NavigationMeshSourceGeometryData3D::merge(const Ref<NavigationMeshSourceGeo
}
_projected_obstructions.append_array(other_projected_obstructions);
bounds_dirty = true;
}
void NavigationMeshSourceGeometryData3D::add_projected_obstruction(const Vector<Vector3> &p_vertices, float p_elevation, float p_height, bool p_carve) {
@ -259,6 +267,7 @@ void NavigationMeshSourceGeometryData3D::add_projected_obstruction(const Vector<
RWLockWrite write_lock(geometry_rwlock);
_projected_obstructions.push_back(projected_obstruction);
bounds_dirty = true;
}
void NavigationMeshSourceGeometryData3D::set_projected_obstructions(const Array &p_array) {
@ -285,6 +294,7 @@ void NavigationMeshSourceGeometryData3D::set_projected_obstructions(const Array
RWLockWrite write_lock(geometry_rwlock);
_projected_obstructions.push_back(projected_obstruction);
bounds_dirty = true;
}
}
@ -336,6 +346,7 @@ void NavigationMeshSourceGeometryData3D::set_data(const Vector<float> &p_vertice
vertices = p_vertices;
indices = p_indices;
_projected_obstructions = p_projected_obstructions;
bounds_dirty = true;
}
void NavigationMeshSourceGeometryData3D::get_data(Vector<float> &r_vertices, Vector<int> &r_indices, Vector<ProjectedObstruction> &r_projected_obstructions) {
@ -345,6 +356,45 @@ void NavigationMeshSourceGeometryData3D::get_data(Vector<float> &r_vertices, Vec
r_projected_obstructions = _projected_obstructions;
}
AABB NavigationMeshSourceGeometryData3D::get_bounds() {
geometry_rwlock.read_lock();
if (bounds_dirty) {
geometry_rwlock.read_unlock();
RWLockWrite write_lock(geometry_rwlock);
bounds_dirty = false;
bounds = AABB();
bool first_vertex = true;
for (int i = 0; i < vertices.size() / 3; i++) {
const Vector3 vertex = Vector3(vertices[i * 3], vertices[i * 3 + 1], vertices[i * 3 + 2]);
if (first_vertex) {
first_vertex = false;
bounds.position = vertex;
} else {
bounds.expand_to(vertex);
}
}
for (const ProjectedObstruction &projected_obstruction : _projected_obstructions) {
for (int i = 0; i < projected_obstruction.vertices.size() / 3; i++) {
const Vector3 vertex = Vector3(projected_obstruction.vertices[i * 3], projected_obstruction.vertices[i * 3 + 1], projected_obstruction.vertices[i * 3 + 2]);
if (first_vertex) {
first_vertex = false;
bounds.position = vertex;
} else {
bounds.expand_to(vertex);
}
}
}
} else {
geometry_rwlock.read_unlock();
}
RWLockRead read_lock(geometry_rwlock);
return bounds;
}
void NavigationMeshSourceGeometryData3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_vertices", "vertices"), &NavigationMeshSourceGeometryData3D::set_vertices);
ClassDB::bind_method(D_METHOD("get_vertices"), &NavigationMeshSourceGeometryData3D::get_vertices);
@ -367,6 +417,8 @@ void NavigationMeshSourceGeometryData3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_projected_obstructions", "projected_obstructions"), &NavigationMeshSourceGeometryData3D::set_projected_obstructions);
ClassDB::bind_method(D_METHOD("get_projected_obstructions"), &NavigationMeshSourceGeometryData3D::get_projected_obstructions);
ClassDB::bind_method(D_METHOD("get_bounds"), &NavigationMeshSourceGeometryData3D::get_bounds);
ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR3_ARRAY, "vertices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_vertices", "get_vertices");
ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "indices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_indices", "get_indices");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "projected_obstructions", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_projected_obstructions", "get_projected_obstructions");

View file

@ -41,6 +41,9 @@ class NavigationMeshSourceGeometryData3D : public Resource {
Vector<float> vertices;
Vector<int> indices;
AABB bounds;
bool bounds_dirty = true;
public:
struct ProjectedObstruction;
@ -101,6 +104,8 @@ public:
void set_data(const Vector<float> &p_vertices, const Vector<int> &p_indices, Vector<ProjectedObstruction> &p_projected_obstructions);
void get_data(Vector<float> &r_vertices, Vector<int> &r_indices, Vector<ProjectedObstruction> &r_projected_obstructions);
AABB get_bounds();
NavigationMeshSourceGeometryData3D() {}
~NavigationMeshSourceGeometryData3D() { clear(); }
};

View file

@ -31,6 +31,7 @@
#include "primitive_meshes.h"
#include "core/config/project_settings.h"
#include "core/math/math_funcs.h"
#include "scene/resources/theme.h"
#include "scene/theme/theme_db.h"
#include "servers/rendering_server.h"
@ -261,6 +262,9 @@ void PrimitiveMesh::_bind_methods() {
}
void PrimitiveMesh::set_material(const Ref<Material> &p_material) {
if (p_material == material) {
return;
}
material = p_material;
if (!pending_request) {
// just apply it, else it'll happen when _update is called.
@ -279,6 +283,9 @@ Array PrimitiveMesh::get_mesh_arrays() const {
}
void PrimitiveMesh::set_custom_aabb(const AABB &p_custom) {
if (p_custom.is_equal_approx(custom_aabb)) {
return;
}
custom_aabb = p_custom;
RS::get_singleton()->mesh_set_custom_aabb(mesh, custom_aabb);
emit_changed();
@ -289,6 +296,9 @@ AABB PrimitiveMesh::get_custom_aabb() const {
}
void PrimitiveMesh::set_flip_faces(bool p_enable) {
if (p_enable == flip_faces) {
return;
}
flip_faces = p_enable;
request_update();
}
@ -298,12 +308,18 @@ bool PrimitiveMesh::get_flip_faces() const {
}
void PrimitiveMesh::set_add_uv2(bool p_enable) {
if (p_enable == add_uv2) {
return;
}
add_uv2 = p_enable;
_update_lightmap_size();
request_update();
}
void PrimitiveMesh::set_uv2_padding(float p_padding) {
if (Math::is_equal_approx(p_padding, uv2_padding)) {
return;
}
uv2_padding = p_padding;
_update_lightmap_size();
request_update();
@ -324,22 +340,43 @@ Vector2 PrimitiveMesh::get_uv2_scale(Vector2 p_margin_scale) const {
}
float PrimitiveMesh::get_lightmap_texel_size() const {
float texel_size = GLOBAL_GET("rendering/lightmapping/primitive_meshes/texel_size");
if (texel_size <= 0.0) {
texel_size = 0.2;
}
return texel_size;
}
void PrimitiveMesh::_on_settings_changed() {
float new_texel_size = float(GLOBAL_GET("rendering/lightmapping/primitive_meshes/texel_size"));
if (new_texel_size <= 0.0) {
new_texel_size = 0.2;
}
if (texel_size == new_texel_size) {
return;
}
texel_size = new_texel_size;
_update_lightmap_size();
request_update();
}
PrimitiveMesh::PrimitiveMesh() {
ERR_FAIL_NULL(RenderingServer::get_singleton());
mesh = RenderingServer::get_singleton()->mesh_create();
ERR_FAIL_NULL(ProjectSettings::get_singleton());
texel_size = float(GLOBAL_GET("rendering/lightmapping/primitive_meshes/texel_size"));
if (texel_size <= 0.0) {
texel_size = 0.2;
}
ProjectSettings *project_settings = ProjectSettings::get_singleton();
project_settings->connect("settings_changed", callable_mp(this, &PrimitiveMesh::_on_settings_changed));
}
PrimitiveMesh::~PrimitiveMesh() {
ERR_FAIL_NULL(RenderingServer::get_singleton());
RenderingServer::get_singleton()->free(mesh);
ERR_FAIL_NULL(ProjectSettings::get_singleton());
ProjectSettings *project_settings = ProjectSettings::get_singleton();
project_settings->disconnect("settings_changed", callable_mp(this, &PrimitiveMesh::_on_settings_changed));
}
/**
@ -350,7 +387,6 @@ void CapsuleMesh::_update_lightmap_size() {
if (get_add_uv2()) {
// size must have changed, update lightmap size hint
Size2i _lightmap_size_hint;
float texel_size = get_lightmap_texel_size();
float padding = get_uv2_padding();
float radial_length = radius * Math_PI * 0.5; // circumference of 90 degree bend
@ -365,7 +401,6 @@ void CapsuleMesh::_update_lightmap_size() {
void CapsuleMesh::_create_mesh_array(Array &p_arr) const {
bool _add_uv2 = get_add_uv2();
float texel_size = get_lightmap_texel_size();
float _uv2_padding = get_uv2_padding() * texel_size;
create_mesh_array(p_arr, radius, height, radial_segments, rings, _add_uv2, _uv2_padding);
@ -408,19 +443,29 @@ void CapsuleMesh::create_mesh_array(Array &p_arr, const float radius, const floa
v = j;
v /= (rings + 1);
w = sin(0.5 * Math_PI * v);
y = radius * cos(0.5 * Math_PI * v);
if (j == (rings + 1)) {
w = 1.0;
y = 0.0;
} else {
w = Math::sin(0.5 * Math_PI * v);
y = Math::cos(0.5 * Math_PI * v);
}
for (i = 0; i <= radial_segments; i++) {
u = i;
u /= radial_segments;
x = -sin(u * Math_TAU);
z = cos(u * Math_TAU);
if (i == radial_segments) {
x = 0.0;
z = 1.0;
} else {
x = -Math::sin(u * Math_TAU);
z = Math::cos(u * Math_TAU);
}
Vector3 p = Vector3(x * radius * w, y, -z * radius * w);
points.push_back(p + Vector3(0.0, 0.5 * height - radius, 0.0));
normals.push_back(p.normalized());
Vector3 p = Vector3(x * w, y, -z * w);
points.push_back(p * radius + Vector3(0.0, 0.5 * height - radius, 0.0));
normals.push_back(p);
ADD_TANGENT(-z, 0.0, -x, 1.0)
uvs.push_back(Vector2(u, v * onethird));
if (p_add_uv2) {
@ -457,8 +502,13 @@ void CapsuleMesh::create_mesh_array(Array &p_arr, const float radius, const floa
u = i;
u /= radial_segments;
x = -sin(u * Math_TAU);
z = cos(u * Math_TAU);
if (i == radial_segments) {
x = 0.0;
z = 1.0;
} else {
x = -Math::sin(u * Math_TAU);
z = Math::cos(u * Math_TAU);
}
Vector3 p = Vector3(x * radius, y, -z * radius);
points.push_back(p);
@ -492,24 +542,33 @@ void CapsuleMesh::create_mesh_array(Array &p_arr, const float radius, const floa
v = j;
v /= (rings + 1);
v += 1.0;
w = sin(0.5 * Math_PI * v);
y = radius * cos(0.5 * Math_PI * v);
if (j == (rings + 1)) {
w = 0.0;
y = -1.0;
} else {
w = Math::cos(0.5 * Math_PI * v);
y = -Math::sin(0.5 * Math_PI * v);
}
for (i = 0; i <= radial_segments; i++) {
u = i;
u /= radial_segments;
x = -sin(u * Math_TAU);
z = cos(u * Math_TAU);
if (i == radial_segments) {
x = 0.0;
z = 1.0;
} else {
x = -Math::sin(u * Math_TAU);
z = Math::cos(u * Math_TAU);
}
Vector3 p = Vector3(x * radius * w, y, -z * radius * w);
points.push_back(p + Vector3(0.0, -0.5 * height + radius, 0.0));
normals.push_back(p.normalized());
Vector3 p = Vector3(x * w, y, -z * w);
points.push_back(p * radius + Vector3(0.0, -0.5 * height + radius, 0.0));
normals.push_back(p);
ADD_TANGENT(-z, 0.0, -x, 1.0)
uvs.push_back(Vector2(u, twothirds + ((v - 1.0) * onethird)));
uvs.push_back(Vector2(u, twothirds + v * onethird));
if (p_add_uv2) {
uv2s.push_back(Vector2(u * radial_h, radial_v + height_v + ((v - 1.0) * radial_v)));
uv2s.push_back(Vector2(u * radial_h, radial_v + height_v + v * radial_v));
}
point++;
@ -559,6 +618,10 @@ void CapsuleMesh::_bind_methods() {
}
void CapsuleMesh::set_radius(const float p_radius) {
if (Math::is_equal_approx(radius, p_radius)) {
return;
}
radius = p_radius;
if (radius > height * 0.5) {
height = radius * 2.0;
@ -572,6 +635,10 @@ float CapsuleMesh::get_radius() const {
}
void CapsuleMesh::set_height(const float p_height) {
if (Math::is_equal_approx(height, p_height)) {
return;
}
height = p_height;
if (radius > height * 0.5) {
radius = height * 0.5;
@ -585,6 +652,10 @@ float CapsuleMesh::get_height() const {
}
void CapsuleMesh::set_radial_segments(const int p_segments) {
if (radial_segments == p_segments) {
return;
}
radial_segments = p_segments > 4 ? p_segments : 4;
request_update();
}
@ -594,6 +665,10 @@ int CapsuleMesh::get_radial_segments() const {
}
void CapsuleMesh::set_rings(const int p_rings) {
if (rings == p_rings) {
return;
}
ERR_FAIL_COND(p_rings < 0);
rings = p_rings;
request_update();
@ -613,7 +688,6 @@ void BoxMesh::_update_lightmap_size() {
if (get_add_uv2()) {
// size must have changed, update lightmap size hint
Size2i _lightmap_size_hint;
float texel_size = get_lightmap_texel_size();
float padding = get_uv2_padding();
float width = (size.x + size.z) / texel_size;
@ -632,7 +706,6 @@ void BoxMesh::_create_mesh_array(Array &p_arr) const {
// With 3 faces along the width and 2 along the height of the texture we need to adjust our scale
// accordingly.
bool _add_uv2 = get_add_uv2();
float texel_size = get_lightmap_texel_size();
float _uv2_padding = get_uv2_padding() * texel_size;
BoxMesh::create_mesh_array(p_arr, size, subdivide_w, subdivide_h, subdivide_d, _add_uv2, _uv2_padding);
@ -891,6 +964,10 @@ void BoxMesh::_bind_methods() {
}
void BoxMesh::set_size(const Vector3 &p_size) {
if (p_size.is_equal_approx(size)) {
return;
}
size = p_size;
_update_lightmap_size();
request_update();
@ -901,6 +978,10 @@ Vector3 BoxMesh::get_size() const {
}
void BoxMesh::set_subdivide_width(const int p_divisions) {
if (p_divisions == subdivide_w) {
return;
}
subdivide_w = p_divisions > 0 ? p_divisions : 0;
request_update();
}
@ -910,6 +991,10 @@ int BoxMesh::get_subdivide_width() const {
}
void BoxMesh::set_subdivide_height(const int p_divisions) {
if (p_divisions == subdivide_h) {
return;
}
subdivide_h = p_divisions > 0 ? p_divisions : 0;
request_update();
}
@ -919,6 +1004,10 @@ int BoxMesh::get_subdivide_height() const {
}
void BoxMesh::set_subdivide_depth(const int p_divisions) {
if (p_divisions == subdivide_d) {
return;
}
subdivide_d = p_divisions > 0 ? p_divisions : 0;
request_update();
}
@ -937,7 +1026,6 @@ void CylinderMesh::_update_lightmap_size() {
if (get_add_uv2()) {
// size must have changed, update lightmap size hint
Size2i _lightmap_size_hint;
float texel_size = get_lightmap_texel_size();
float padding = get_uv2_padding();
float top_circumference = top_radius * Math_PI * 2.0;
@ -957,7 +1045,6 @@ void CylinderMesh::_update_lightmap_size() {
void CylinderMesh::_create_mesh_array(Array &p_arr) const {
bool _add_uv2 = get_add_uv2();
float texel_size = get_lightmap_texel_size();
float _uv2_padding = get_uv2_padding() * texel_size;
create_mesh_array(p_arr, top_radius, bottom_radius, height, radial_segments, rings, cap_top, cap_bottom, _add_uv2, _uv2_padding);
@ -974,11 +1061,11 @@ void CylinderMesh::create_mesh_array(Array &p_arr, float top_radius, float botto
float height_v = height / vertical_length;
float padding_v = p_uv2_padding / vertical_length;
float horizonal_length = MAX(MAX(2.0 * (top_radius + bottom_radius + p_uv2_padding), top_circumference + p_uv2_padding), bottom_circumference + p_uv2_padding);
float center_h = 0.5 * (horizonal_length - p_uv2_padding) / horizonal_length;
float top_h = top_circumference / horizonal_length;
float bottom_h = bottom_circumference / horizonal_length;
float padding_h = p_uv2_padding / horizonal_length;
float horizontal_length = MAX(MAX(2.0 * (top_radius + bottom_radius + p_uv2_padding), top_circumference + p_uv2_padding), bottom_circumference + p_uv2_padding);
float center_h = 0.5 * (horizontal_length - p_uv2_padding) / horizontal_length;
float top_h = top_circumference / horizontal_length;
float bottom_h = bottom_circumference / horizontal_length;
float padding_h = p_uv2_padding / horizontal_length;
Vector<Vector3> points;
Vector<Vector3> normals;
@ -1011,8 +1098,13 @@ void CylinderMesh::create_mesh_array(Array &p_arr, float top_radius, float botto
u = i;
u /= radial_segments;
x = sin(u * Math_TAU);
z = cos(u * Math_TAU);
if (i == radial_segments) {
x = 0.0;
z = 1.0;
} else {
x = Math::sin(u * Math_TAU);
z = Math::cos(u * Math_TAU);
}
Vector3 p = Vector3(x * radius, y, z * radius);
points.push_back(p);
@ -1040,9 +1132,9 @@ void CylinderMesh::create_mesh_array(Array &p_arr, float top_radius, float botto
}
// Adjust for bottom section, only used if we calculate UV2s.
top_h = top_radius / horizonal_length;
top_h = top_radius / horizontal_length;
float top_v = top_radius / vertical_length;
bottom_h = bottom_radius / horizonal_length;
bottom_h = bottom_radius / horizontal_length;
float bottom_v = bottom_radius / vertical_length;
// Add top.
@ -1063,8 +1155,13 @@ void CylinderMesh::create_mesh_array(Array &p_arr, float top_radius, float botto
float r = i;
r /= radial_segments;
x = sin(r * Math_TAU);
z = cos(r * Math_TAU);
if (i == radial_segments) {
x = 0.0;
z = 1.0;
} else {
x = Math::sin(r * Math_TAU);
z = Math::cos(r * Math_TAU);
}
u = ((x + 1.0) * 0.25);
v = 0.5 + ((z + 1.0) * 0.25);
@ -1105,8 +1202,13 @@ void CylinderMesh::create_mesh_array(Array &p_arr, float top_radius, float botto
float r = i;
r /= radial_segments;
x = sin(r * Math_TAU);
z = cos(r * Math_TAU);
if (i == radial_segments) {
x = 0.0;
z = 1.0;
} else {
x = Math::sin(r * Math_TAU);
z = Math::cos(r * Math_TAU);
}
u = 0.5 + ((x + 1.0) * 0.25);
v = 1.0 - ((z + 1.0) * 0.25);
@ -1168,6 +1270,10 @@ void CylinderMesh::_bind_methods() {
}
void CylinderMesh::set_top_radius(const float p_radius) {
if (Math::is_equal_approx(p_radius, top_radius)) {
return;
}
top_radius = p_radius;
_update_lightmap_size();
request_update();
@ -1178,6 +1284,10 @@ float CylinderMesh::get_top_radius() const {
}
void CylinderMesh::set_bottom_radius(const float p_radius) {
if (Math::is_equal_approx(p_radius, bottom_radius)) {
return;
}
bottom_radius = p_radius;
_update_lightmap_size();
request_update();
@ -1188,6 +1298,10 @@ float CylinderMesh::get_bottom_radius() const {
}
void CylinderMesh::set_height(const float p_height) {
if (Math::is_equal_approx(p_height, height)) {
return;
}
height = p_height;
_update_lightmap_size();
request_update();
@ -1198,6 +1312,10 @@ float CylinderMesh::get_height() const {
}
void CylinderMesh::set_radial_segments(const int p_segments) {
if (p_segments == radial_segments) {
return;
}
radial_segments = p_segments > 4 ? p_segments : 4;
request_update();
}
@ -1207,6 +1325,10 @@ int CylinderMesh::get_radial_segments() const {
}
void CylinderMesh::set_rings(const int p_rings) {
if (p_rings == rings) {
return;
}
ERR_FAIL_COND(p_rings < 0);
rings = p_rings;
request_update();
@ -1217,6 +1339,10 @@ int CylinderMesh::get_rings() const {
}
void CylinderMesh::set_cap_top(bool p_cap_top) {
if (p_cap_top == cap_top) {
return;
}
cap_top = p_cap_top;
request_update();
}
@ -1226,6 +1352,10 @@ bool CylinderMesh::is_cap_top() const {
}
void CylinderMesh::set_cap_bottom(bool p_cap_bottom) {
if (p_cap_bottom == cap_bottom) {
return;
}
cap_bottom = p_cap_bottom;
request_update();
}
@ -1244,7 +1374,6 @@ void PlaneMesh::_update_lightmap_size() {
if (get_add_uv2()) {
// size must have changed, update lightmap size hint
Size2i _lightmap_size_hint;
float texel_size = get_lightmap_texel_size();
float padding = get_uv2_padding();
_lightmap_size_hint.x = MAX(1.0, (size.x / texel_size) + padding);
@ -1361,6 +1490,9 @@ void PlaneMesh::_bind_methods() {
}
void PlaneMesh::set_size(const Size2 &p_size) {
if (p_size == size) {
return;
}
size = p_size;
_update_lightmap_size();
request_update();
@ -1371,6 +1503,9 @@ Size2 PlaneMesh::get_size() const {
}
void PlaneMesh::set_subdivide_width(const int p_divisions) {
if (p_divisions == subdivide_w || (subdivide_w == 0 && p_divisions < 0)) {
return;
}
subdivide_w = p_divisions > 0 ? p_divisions : 0;
request_update();
}
@ -1380,6 +1515,9 @@ int PlaneMesh::get_subdivide_width() const {
}
void PlaneMesh::set_subdivide_depth(const int p_divisions) {
if (p_divisions == subdivide_d || (subdivide_d == 0 && p_divisions < 0)) {
return;
}
subdivide_d = p_divisions > 0 ? p_divisions : 0;
request_update();
}
@ -1389,6 +1527,9 @@ int PlaneMesh::get_subdivide_depth() const {
}
void PlaneMesh::set_center_offset(const Vector3 p_offset) {
if (p_offset.is_equal_approx(center_offset)) {
return;
}
center_offset = p_offset;
request_update();
}
@ -1398,6 +1539,9 @@ Vector3 PlaneMesh::get_center_offset() const {
}
void PlaneMesh::set_orientation(const Orientation p_orientation) {
if (p_orientation == orientation) {
return;
}
orientation = p_orientation;
request_update();
}
@ -1416,7 +1560,6 @@ void PrismMesh::_update_lightmap_size() {
if (get_add_uv2()) {
// size must have changed, update lightmap size hint
Size2i _lightmap_size_hint;
float texel_size = get_lightmap_texel_size();
float padding = get_uv2_padding();
// left_to_right does not effect the surface area of the prism so we ignore that.
@ -1440,7 +1583,6 @@ void PrismMesh::_create_mesh_array(Array &p_arr) const {
// Only used if we calculate UV2
bool _add_uv2 = get_add_uv2();
float texel_size = get_lightmap_texel_size();
float _uv2_padding = get_uv2_padding() * texel_size;
float horizontal_total = size.x + size.z + 2.0 * _uv2_padding;
@ -1707,6 +1849,9 @@ void PrismMesh::_bind_methods() {
}
void PrismMesh::set_left_to_right(const float p_left_to_right) {
if (Math::is_equal_approx(p_left_to_right, left_to_right)) {
return;
}
left_to_right = p_left_to_right;
request_update();
}
@ -1716,6 +1861,9 @@ float PrismMesh::get_left_to_right() const {
}
void PrismMesh::set_size(const Vector3 &p_size) {
if (p_size.is_equal_approx(size)) {
return;
}
size = p_size;
_update_lightmap_size();
request_update();
@ -1726,6 +1874,9 @@ Vector3 PrismMesh::get_size() const {
}
void PrismMesh::set_subdivide_width(const int p_divisions) {
if (p_divisions == subdivide_w || (p_divisions < 0 && subdivide_w == 0)) {
return;
}
subdivide_w = p_divisions > 0 ? p_divisions : 0;
request_update();
}
@ -1735,6 +1886,9 @@ int PrismMesh::get_subdivide_width() const {
}
void PrismMesh::set_subdivide_height(const int p_divisions) {
if (p_divisions == subdivide_h || (p_divisions < 0 && subdivide_h == 0)) {
return;
}
subdivide_h = p_divisions > 0 ? p_divisions : 0;
request_update();
}
@ -1744,6 +1898,9 @@ int PrismMesh::get_subdivide_height() const {
}
void PrismMesh::set_subdivide_depth(const int p_divisions) {
if (p_divisions == subdivide_d || (p_divisions < 0 && subdivide_d == 0)) {
return;
}
subdivide_d = p_divisions > 0 ? p_divisions : 0;
request_update();
}
@ -1762,7 +1919,6 @@ void SphereMesh::_update_lightmap_size() {
if (get_add_uv2()) {
// size must have changed, update lightmap size hint
Size2i _lightmap_size_hint;
float texel_size = get_lightmap_texel_size();
float padding = get_uv2_padding();
float _width = radius * Math_TAU;
@ -1776,7 +1932,6 @@ void SphereMesh::_update_lightmap_size() {
void SphereMesh::_create_mesh_array(Array &p_arr) const {
bool _add_uv2 = get_add_uv2();
float texel_size = get_lightmap_texel_size();
float _uv2_padding = get_uv2_padding() * texel_size;
create_mesh_array(p_arr, radius, height, radial_segments, rings, is_hemisphere, _add_uv2, _uv2_padding);
@ -1786,14 +1941,14 @@ void SphereMesh::create_mesh_array(Array &p_arr, float radius, float height, int
int i, j, prevrow, thisrow, point;
float x, y, z;
float scale = height * (is_hemisphere ? 1.0 : 0.5);
float scale = height / radius * (is_hemisphere ? 1.0 : 0.5);
// Only used if we calculate UV2
float circumference = radius * Math_TAU;
float horizontal_length = circumference + p_uv2_padding;
float center_h = 0.5 * circumference / horizontal_length;
float height_v = scale * Math_PI / ((scale * Math_PI) + p_uv2_padding);
float height_v = scale * Math_PI / ((scale * Math_PI) + p_uv2_padding / radius);
// set our bounding box
@ -1818,23 +1973,33 @@ void SphereMesh::create_mesh_array(Array &p_arr, float radius, float height, int
float w;
v /= (rings + 1);
w = sin(Math_PI * v);
y = scale * cos(Math_PI * v);
if (j == (rings + 1)) {
w = 0.0;
y = -1.0;
} else {
w = Math::sin(Math_PI * v);
y = Math::cos(Math_PI * v);
}
for (i = 0; i <= radial_segments; i++) {
float u = i;
u /= radial_segments;
x = sin(u * Math_TAU);
z = cos(u * Math_TAU);
if (i == radial_segments) {
x = 0.0;
z = 1.0;
} else {
x = Math::sin(u * Math_TAU);
z = Math::cos(u * Math_TAU);
}
if (is_hemisphere && y < 0.0) {
points.push_back(Vector3(x * radius * w, 0.0, z * radius * w));
normals.push_back(Vector3(0.0, -1.0, 0.0));
} else {
Vector3 p = Vector3(x * radius * w, y, z * radius * w);
points.push_back(p);
Vector3 normal = Vector3(x * w * scale, radius * (y / scale), z * w * scale);
Vector3 p = Vector3(x * w, y * scale, z * w);
points.push_back(p * radius);
Vector3 normal = Vector3(x * w * scale, y, z * w * scale);
normals.push_back(normal.normalized());
}
ADD_TANGENT(z, 0.0, -x, 1.0)
@ -1892,6 +2057,9 @@ void SphereMesh::_bind_methods() {
}
void SphereMesh::set_radius(const float p_radius) {
if (Math::is_equal_approx(p_radius, radius)) {
return;
}
radius = p_radius;
_update_lightmap_size();
request_update();
@ -1902,6 +2070,9 @@ float SphereMesh::get_radius() const {
}
void SphereMesh::set_height(const float p_height) {
if (Math::is_equal_approx(height, p_height)) {
return;
}
height = p_height;
_update_lightmap_size();
request_update();
@ -1912,6 +2083,9 @@ float SphereMesh::get_height() const {
}
void SphereMesh::set_radial_segments(const int p_radial_segments) {
if (p_radial_segments == radial_segments || (radial_segments == 4 && p_radial_segments < 4)) {
return;
}
radial_segments = p_radial_segments > 4 ? p_radial_segments : 4;
request_update();
}
@ -1921,6 +2095,9 @@ int SphereMesh::get_radial_segments() const {
}
void SphereMesh::set_rings(const int p_rings) {
if (p_rings == rings) {
return;
}
ERR_FAIL_COND(p_rings < 1);
rings = p_rings;
request_update();
@ -1931,6 +2108,9 @@ int SphereMesh::get_rings() const {
}
void SphereMesh::set_is_hemisphere(const bool p_is_hemisphere) {
if (p_is_hemisphere == is_hemisphere) {
return;
}
is_hemisphere = p_is_hemisphere;
_update_lightmap_size();
request_update();
@ -1950,7 +2130,6 @@ void TorusMesh::_update_lightmap_size() {
if (get_add_uv2()) {
// size must have changed, update lightmap size hint
Size2i _lightmap_size_hint;
float texel_size = get_lightmap_texel_size();
float padding = get_uv2_padding();
float min_radius = inner_radius;
@ -2000,7 +2179,6 @@ void TorusMesh::_create_mesh_array(Array &p_arr) const {
// Only used if we calculate UV2
bool _add_uv2 = get_add_uv2();
float texel_size = get_lightmap_texel_size();
float _uv2_padding = get_uv2_padding() * texel_size;
float horizontal_total = max_radius * Math_TAU + _uv2_padding;
@ -2015,13 +2193,13 @@ void TorusMesh::_create_mesh_array(Array &p_arr) const {
float inci = float(i) / rings;
float angi = inci * Math_TAU;
Vector2 normali = Vector2(-Math::sin(angi), -Math::cos(angi));
Vector2 normali = (i == rings) ? Vector2(0.0, -1.0) : Vector2(-Math::sin(angi), -Math::cos(angi));
for (int j = 0; j <= ring_segments; j++) {
float incj = float(j) / ring_segments;
float angj = incj * Math_TAU;
Vector2 normalj = Vector2(-Math::cos(angj), Math::sin(angj));
Vector2 normalj = (j == ring_segments) ? Vector2(-1.0, 0.0) : Vector2(-Math::cos(angj), Math::sin(angj));
Vector2 normalk = normalj * radius + Vector2(min_radius + radius, 0);
float offset_h = 0.5 * (1.0 - normalj.x) * delta_h;
@ -2030,7 +2208,7 @@ void TorusMesh::_create_mesh_array(Array &p_arr) const {
points.push_back(Vector3(normali.x * normalk.x, normalk.y, normali.y * normalk.x));
normals.push_back(Vector3(normali.x * normalj.x, normalj.y, normali.y * normalj.x));
ADD_TANGENT(-Math::cos(angi), 0.0, Math::sin(angi), 1.0);
ADD_TANGENT(normali.y, 0.0, -normali.x, 1.0);
uvs.push_back(Vector2(inci, incj));
if (_add_uv2) {
uv2s.push_back(Vector2(offset_h + inci * adj_h, incj * height_v));
@ -2078,6 +2256,9 @@ void TorusMesh::_bind_methods() {
}
void TorusMesh::set_inner_radius(const float p_inner_radius) {
if (Math::is_equal_approx(p_inner_radius, inner_radius)) {
return;
}
inner_radius = p_inner_radius;
request_update();
}
@ -2087,6 +2268,9 @@ float TorusMesh::get_inner_radius() const {
}
void TorusMesh::set_outer_radius(const float p_outer_radius) {
if (Math::is_equal_approx(p_outer_radius, outer_radius)) {
return;
}
outer_radius = p_outer_radius;
request_update();
}
@ -2096,6 +2280,9 @@ float TorusMesh::get_outer_radius() const {
}
void TorusMesh::set_rings(const int p_rings) {
if (p_rings == rings) {
return;
}
ERR_FAIL_COND(p_rings < 3);
rings = p_rings;
request_update();
@ -2106,6 +2293,9 @@ int TorusMesh::get_rings() const {
}
void TorusMesh::set_ring_segments(const int p_ring_segments) {
if (p_ring_segments == ring_segments) {
return;
}
ERR_FAIL_COND(p_ring_segments < 3);
ring_segments = p_ring_segments;
request_update();
@ -2135,6 +2325,9 @@ PointMesh::PointMesh() {
// TUBE TRAIL
void TubeTrailMesh::set_radius(const float p_radius) {
if (Math::is_equal_approx(p_radius, radius)) {
return;
}
radius = p_radius;
request_update();
}
@ -2143,6 +2336,9 @@ float TubeTrailMesh::get_radius() const {
}
void TubeTrailMesh::set_radial_steps(const int p_radial_steps) {
if (p_radial_steps == radial_steps) {
return;
}
ERR_FAIL_COND(p_radial_steps < 3 || p_radial_steps > 128);
radial_steps = p_radial_steps;
request_update();
@ -2152,6 +2348,9 @@ int TubeTrailMesh::get_radial_steps() const {
}
void TubeTrailMesh::set_sections(const int p_sections) {
if (p_sections == sections) {
return;
}
ERR_FAIL_COND(p_sections < 2 || p_sections > 128);
sections = p_sections;
request_update();
@ -2161,6 +2360,9 @@ int TubeTrailMesh::get_sections() const {
}
void TubeTrailMesh::set_section_length(float p_section_length) {
if (p_section_length == section_length) {
return;
}
section_length = p_section_length;
request_update();
}
@ -2169,6 +2371,9 @@ float TubeTrailMesh::get_section_length() const {
}
void TubeTrailMesh::set_section_rings(const int p_section_rings) {
if (p_section_rings == section_rings) {
return;
}
ERR_FAIL_COND(p_section_rings < 1 || p_section_rings > 1024);
section_rings = p_section_rings;
request_update();
@ -2178,6 +2383,9 @@ int TubeTrailMesh::get_section_rings() const {
}
void TubeTrailMesh::set_cap_top(bool p_cap_top) {
if (p_cap_top == cap_top) {
return;
}
cap_top = p_cap_top;
request_update();
}
@ -2187,6 +2395,9 @@ bool TubeTrailMesh::is_cap_top() const {
}
void TubeTrailMesh::set_cap_bottom(bool p_cap_bottom) {
if (p_cap_bottom == cap_bottom) {
return;
}
cap_bottom = p_cap_bottom;
request_update();
}
@ -2272,8 +2483,12 @@ void TubeTrailMesh::_create_mesh_array(Array &p_arr) const {
if (curve.is_valid() && curve->get_point_count() > 0) {
r *= curve->sample_baked(v);
}
float x = sin(u * Math_TAU);
float z = cos(u * Math_TAU);
float x = 0.0;
float z = 1.0;
if (i < radial_steps) {
x = Math::sin(u * Math_TAU);
z = Math::cos(u * Math_TAU);
}
Vector3 p = Vector3(x * r, y, z * r);
points.push_back(p);
@ -2341,8 +2556,12 @@ void TubeTrailMesh::_create_mesh_array(Array &p_arr) const {
float r = i;
r /= radial_steps;
float x = sin(r * Math_TAU);
float z = cos(r * Math_TAU);
float x = 0.0;
float z = 1.0;
if (i < radial_steps) {
x = Math::sin(r * Math_TAU);
z = Math::cos(r * Math_TAU);
}
float u = ((x + 1.0) * 0.25);
float v = 0.5 + ((z + 1.0) * 0.25);
@ -2406,8 +2625,12 @@ void TubeTrailMesh::_create_mesh_array(Array &p_arr) const {
float r = i;
r /= radial_steps;
float x = sin(r * Math_TAU);
float z = cos(r * Math_TAU);
float x = 0.0;
float z = 1.0;
if (i < radial_steps) {
x = Math::sin(r * Math_TAU);
z = Math::cos(r * Math_TAU);
}
float u = 0.5 + ((x + 1.0) * 0.25);
float v = 1.0 - ((z + 1.0) * 0.25);
@ -2493,6 +2716,9 @@ TubeTrailMesh::TubeTrailMesh() {
// RIBBON TRAIL
void RibbonTrailMesh::set_shape(Shape p_shape) {
if (p_shape == shape) {
return;
}
shape = p_shape;
request_update();
}
@ -2501,6 +2727,9 @@ RibbonTrailMesh::Shape RibbonTrailMesh::get_shape() const {
}
void RibbonTrailMesh::set_size(const float p_size) {
if (Math::is_equal_approx(p_size, size)) {
return;
}
size = p_size;
request_update();
}
@ -2509,6 +2738,9 @@ float RibbonTrailMesh::get_size() const {
}
void RibbonTrailMesh::set_sections(const int p_sections) {
if (p_sections == sections) {
return;
}
ERR_FAIL_COND(p_sections < 2 || p_sections > 128);
sections = p_sections;
request_update();
@ -2518,6 +2750,9 @@ int RibbonTrailMesh::get_sections() const {
}
void RibbonTrailMesh::set_section_length(float p_section_length) {
if (p_section_length == section_length) {
return;
}
section_length = p_section_length;
request_update();
}
@ -2526,6 +2761,9 @@ float RibbonTrailMesh::get_section_length() const {
}
void RibbonTrailMesh::set_section_segments(const int p_section_segments) {
if (p_section_segments == section_segments) {
return;
}
ERR_FAIL_COND(p_section_segments < 1 || p_section_segments > 1024);
section_segments = p_section_segments;
request_update();
@ -3468,13 +3706,13 @@ Ref<Font> TextMesh::_get_font_or_default() const {
}
StringName theme_name = "font";
List<StringName> theme_types;
ThemeDB::get_singleton()->get_native_type_dependencies(get_class_name(), &theme_types);
Vector<StringName> theme_types;
ThemeDB::get_singleton()->get_native_type_dependencies(get_class_name(), theme_types);
ThemeContext *global_context = ThemeDB::get_singleton()->get_default_theme_context();
List<Ref<Theme>> themes = global_context->get_themes();
Vector<Ref<Theme>> themes = global_context->get_themes();
if (Engine::get_singleton()->is_editor_hint()) {
themes.push_front(ThemeDB::get_singleton()->get_project_theme());
themes.insert(0, ThemeDB::get_singleton()->get_project_theme());
}
for (const Ref<Theme> &theme : themes) {

View file

@ -67,6 +67,9 @@ protected:
// assume primitive triangles as the type, correct for all but one and it will change this :)
Mesh::PrimitiveType primitive_type = Mesh::PRIMITIVE_TRIANGLES;
// Copy of our texel_size project setting.
float texel_size = 0.2;
static void _bind_methods();
virtual void _create_mesh_array(Array &p_arr) const {}
@ -74,7 +77,9 @@ protected:
Vector2 get_uv2_scale(Vector2 p_margin_scale = Vector2(1.0, 1.0)) const;
float get_lightmap_texel_size() const;
virtual void _update_lightmap_size(){};
virtual void _update_lightmap_size() {}
void _on_settings_changed();
public:
virtual int get_surface_count() const override;
@ -536,17 +541,17 @@ private:
Vector2 point;
bool sharp = false;
ContourPoint(){};
ContourPoint() {}
ContourPoint(const Vector2 &p_pt, bool p_sharp) {
point = p_pt;
sharp = p_sharp;
};
}
};
struct ContourInfo {
real_t length = 0.0;
bool ccw = true;
ContourInfo(){};
ContourInfo() {}
ContourInfo(real_t p_len, bool p_ccw) {
length = p_len;
ccw = p_ccw;

View file

@ -30,6 +30,7 @@
#include "separation_ray_shape_3d.h"
#include "scene/resources/mesh.h"
#include "servers/physics_server_3d.h"
Vector<Vector3> SeparationRayShape3D::get_debug_mesh_lines() const {
@ -41,6 +42,10 @@ Vector<Vector3> SeparationRayShape3D::get_debug_mesh_lines() const {
return points;
}
Ref<ArrayMesh> SeparationRayShape3D::get_debug_arraymesh_faces(const Color &p_modulate) const {
return memnew(ArrayMesh);
}
real_t SeparationRayShape3D::get_enclosing_radius() const {
return length;
}

View file

@ -33,6 +33,8 @@
#include "scene/resources/3d/shape_3d.h"
class ArrayMesh;
class SeparationRayShape3D : public Shape3D {
GDCLASS(SeparationRayShape3D, Shape3D);
float length = 1.0;
@ -50,6 +52,7 @@ public:
bool get_slide_on_slope() const;
virtual Vector<Vector3> get_debug_mesh_lines() const override;
virtual Ref<ArrayMesh> get_debug_arraymesh_faces(const Color &p_modulate) const override;
virtual real_t get_enclosing_radius() const override;
SeparationRayShape3D();

View file

@ -30,7 +30,6 @@
#include "shape_3d.h"
#include "core/os/os.h"
#include "scene/main/scene_tree.h"
#include "scene/resources/mesh.h"
#include "servers/physics_server_3d.h"
@ -66,6 +65,38 @@ void Shape3D::set_margin(real_t p_margin) {
PhysicsServer3D::get_singleton()->shape_set_margin(shape, margin);
}
void Shape3D::set_debug_color(const Color &p_color) {
if (p_color == debug_color) {
return;
}
debug_color = p_color;
#ifdef DEBUG_ENABLED
debug_properties_edited = true;
#endif // DEBUG_ENABLED
_update_shape();
}
Color Shape3D::get_debug_color() const {
return debug_color;
}
void Shape3D::set_debug_fill(bool p_fill) {
if (p_fill == debug_fill) {
return;
}
debug_fill = p_fill;
#ifdef DEBUG_ENABLED
debug_properties_edited = true;
#endif // DEBUG_ENABLED
_update_shape();
}
bool Shape3D::get_debug_fill() const {
return debug_fill;
}
Ref<ArrayMesh> Shape3D::get_debug_mesh() {
if (debug_mesh_cache.is_valid()) {
return debug_mesh_cache;
@ -73,35 +104,66 @@ Ref<ArrayMesh> Shape3D::get_debug_mesh() {
Vector<Vector3> lines = get_debug_mesh_lines();
debug_mesh_cache = Ref<ArrayMesh>(memnew(ArrayMesh));
debug_mesh_cache.instantiate();
if (!lines.is_empty()) {
//make mesh
Vector<Vector3> array;
array.resize(lines.size());
{
Vector3 *w = array.ptrw();
for (int i = 0; i < lines.size(); i++) {
w[i] = lines[i];
}
Vector3 *v = array.ptrw();
Vector<Color> arraycol;
arraycol.resize(lines.size());
Color *c = arraycol.ptrw();
for (int i = 0; i < lines.size(); i++) {
v[i] = lines[i];
c[i] = debug_color;
}
Array arr;
arr.resize(Mesh::ARRAY_MAX);
arr[Mesh::ARRAY_VERTEX] = array;
Array lines_array;
lines_array.resize(Mesh::ARRAY_MAX);
lines_array[Mesh::ARRAY_VERTEX] = array;
lines_array[Mesh::ARRAY_COLOR] = arraycol;
SceneTree *st = Object::cast_to<SceneTree>(OS::get_singleton()->get_main_loop());
Ref<StandardMaterial3D> material = get_debug_collision_material();
debug_mesh_cache->add_surface_from_arrays(Mesh::PRIMITIVE_LINES, arr);
debug_mesh_cache->add_surface_from_arrays(Mesh::PRIMITIVE_LINES, lines_array);
debug_mesh_cache->surface_set_material(0, material);
if (st) {
debug_mesh_cache->surface_set_material(0, st->get_debug_collision_material());
if (debug_fill) {
Ref<ArrayMesh> array_mesh = get_debug_arraymesh_faces(debug_color * Color(1.0, 1.0, 1.0, 0.0625));
if (array_mesh.is_valid() && array_mesh->get_surface_count() > 0) {
Array solid_array = array_mesh->surface_get_arrays(0);
debug_mesh_cache->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, solid_array);
debug_mesh_cache->surface_set_material(1, material);
}
}
}
return debug_mesh_cache;
}
Ref<Material> Shape3D::get_debug_collision_material() {
if (collision_material.is_valid()) {
return collision_material;
}
Ref<StandardMaterial3D> material = memnew(StandardMaterial3D);
material->set_albedo(Color(1.0, 1.0, 1.0));
material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
material->set_render_priority(StandardMaterial3D::RENDER_PRIORITY_MIN + 1);
material->set_cull_mode(StandardMaterial3D::CULL_BACK);
material->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
material->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
material->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true);
collision_material = material;
return collision_material;
}
void Shape3D::_update_shape() {
emit_changed();
debug_mesh_cache.unref();

View file

@ -34,6 +34,7 @@
#include "core/io/resource.h"
class ArrayMesh;
class Material;
class Shape3D : public Resource {
GDCLASS(Shape3D, Resource);
@ -44,6 +45,14 @@ class Shape3D : public Resource {
real_t margin = 0.04;
Ref<ArrayMesh> debug_mesh_cache;
Ref<Material> collision_material;
// Not wrapped in `#ifdef DEBUG_ENABLED` as it is used for rendering.
Color debug_color = Color(0.0, 0.0, 0.0, 0.0);
bool debug_fill = true;
#ifdef DEBUG_ENABLED
bool debug_properties_edited = false;
#endif // DEBUG_ENABLED
protected:
static void _bind_methods();
@ -51,6 +60,8 @@ protected:
_FORCE_INLINE_ RID get_shape() const { return shape; }
Shape3D(RID p_shape);
Ref<Material> get_debug_collision_material();
virtual void _update_shape();
public:
@ -58,6 +69,7 @@ public:
Ref<ArrayMesh> get_debug_mesh();
virtual Vector<Vector3> get_debug_mesh_lines() const = 0; // { return Vector<Vector3>(); }
virtual Ref<ArrayMesh> get_debug_arraymesh_faces(const Color &p_modulate) const = 0;
/// Returns the radius of a sphere that fully enclose this shape
virtual real_t get_enclosing_radius() const = 0;
@ -69,6 +81,16 @@ public:
real_t get_margin() const;
void set_margin(real_t p_margin);
void set_debug_color(const Color &p_color);
Color get_debug_color() const;
void set_debug_fill(bool p_fill);
bool get_debug_fill() const;
#ifdef DEBUG_ENABLED
_FORCE_INLINE_ bool are_debug_properties_edited() const { return debug_properties_edited; }
#endif // DEBUG_ENABLED
Shape3D();
~Shape3D();
};

View file

@ -269,7 +269,7 @@ void ProceduralSkyMaterial::cleanup_shader() {
}
void ProceduralSkyMaterial::_update_shader() {
shader_mutex.lock();
MutexLock shader_lock(shader_mutex);
if (shader_cache[0].is_null()) {
for (int i = 0; i < 2; i++) {
shader_cache[i] = RS::get_singleton()->shader_create();
@ -354,10 +354,10 @@ void sky() {
i ? "render_mode use_debanding;" : ""));
}
}
shader_mutex.unlock();
}
ProceduralSkyMaterial::ProceduralSkyMaterial() {
_set_material(RS::get_singleton()->material_create());
set_sky_top_color(Color(0.385, 0.454, 0.55));
set_sky_horizon_color(Color(0.6463, 0.6558, 0.6708));
set_sky_curve(0.15);
@ -463,7 +463,7 @@ void PanoramaSkyMaterial::cleanup_shader() {
}
void PanoramaSkyMaterial::_update_shader() {
shader_mutex.lock();
MutexLock shader_lock(shader_mutex);
if (shader_cache[0].is_null()) {
for (int i = 0; i < 2; i++) {
shader_cache[i] = RS::get_singleton()->shader_create();
@ -484,11 +484,10 @@ void sky() {
i ? "filter_linear" : "filter_nearest"));
}
}
shader_mutex.unlock();
}
PanoramaSkyMaterial::PanoramaSkyMaterial() {
_set_material(RS::get_singleton()->material_create());
set_energy_multiplier(1.0);
}
@ -692,7 +691,7 @@ void PhysicalSkyMaterial::cleanup_shader() {
}
void PhysicalSkyMaterial::_update_shader() {
shader_mutex.lock();
MutexLock shader_lock(shader_mutex);
if (shader_cache[0].is_null()) {
for (int i = 0; i < 2; i++) {
shader_cache[i] = RS::get_singleton()->shader_create();
@ -785,11 +784,10 @@ void sky() {
i ? "render_mode use_debanding;" : ""));
}
}
shader_mutex.unlock();
}
PhysicalSkyMaterial::PhysicalSkyMaterial() {
_set_material(RS::get_singleton()->material_create());
set_rayleigh_coefficient(2.0);
set_rayleigh_color(Color(0.3, 0.405, 0.6));
set_mie_coefficient(0.005);

View file

@ -30,6 +30,7 @@
#include "sphere_shape_3d.h"
#include "scene/resources/3d/primitive_meshes.h"
#include "servers/physics_server_3d.h"
Vector<Vector3> SphereShape3D::get_debug_mesh_lines() const {
@ -54,6 +55,24 @@ Vector<Vector3> SphereShape3D::get_debug_mesh_lines() const {
return points;
}
Ref<ArrayMesh> SphereShape3D::get_debug_arraymesh_faces(const Color &p_modulate) const {
Array sphere_array;
sphere_array.resize(RS::ARRAY_MAX);
SphereMesh::create_mesh_array(sphere_array, radius, radius * 2, 32);
Vector<Color> colors;
const PackedVector3Array &verts = sphere_array[RS::ARRAY_VERTEX];
const int32_t verts_size = verts.size();
for (int i = 0; i < verts_size; i++) {
colors.append(p_modulate);
}
Ref<ArrayMesh> sphere_mesh = memnew(ArrayMesh);
sphere_array[RS::ARRAY_COLOR] = colors;
sphere_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, sphere_array);
return sphere_mesh;
}
real_t SphereShape3D::get_enclosing_radius() const {
return radius;
}

View file

@ -33,6 +33,8 @@
#include "scene/resources/3d/shape_3d.h"
class ArrayMesh;
class SphereShape3D : public Shape3D {
GDCLASS(SphereShape3D, Shape3D);
float radius = 0.5f;
@ -47,6 +49,7 @@ public:
float get_radius() const;
virtual Vector<Vector3> get_debug_mesh_lines() const override;
virtual Ref<ArrayMesh> get_debug_arraymesh_faces(const Color &p_modulate) const override;
virtual real_t get_enclosing_radius() const override;
SphereShape3D();

View file

@ -32,7 +32,6 @@
#include "core/config/project_settings.h"
#include "scene/3d/camera_3d.h"
#include "scene/3d/visible_on_screen_notifier_3d.h"
#include "scene/resources/camera_attributes.h"
#include "scene/resources/environment.h"
#include "servers/navigation_server_3d.h"

View file

@ -30,6 +30,7 @@
#include "world_boundary_shape_3d.h"
#include "scene/resources/mesh.h"
#include "servers/physics_server_3d.h"
Vector<Vector3> WorldBoundaryShape3D::get_debug_mesh_lines() const {
@ -61,6 +62,53 @@ Vector<Vector3> WorldBoundaryShape3D::get_debug_mesh_lines() const {
return points;
}
Ref<ArrayMesh> WorldBoundaryShape3D::get_debug_arraymesh_faces(const Color &p_modulate) const {
Plane p = get_plane();
Vector3 n1 = p.get_any_perpendicular_normal();
Vector3 n2 = p.normal.cross(n1).normalized();
Vector3 pface[4] = {
p.normal * p.d + n1 * 10.0 + n2 * 10.0,
p.normal * p.d + n1 * 10.0 + n2 * -10.0,
p.normal * p.d + n1 * -10.0 + n2 * -10.0,
p.normal * p.d + n1 * -10.0 + n2 * 10.0,
};
Vector<Vector3> points = {
pface[0],
pface[1],
pface[2],
pface[3],
};
Vector<Color> colors = {
p_modulate,
p_modulate,
p_modulate,
p_modulate,
};
Vector<int> indices = {
0,
1,
2,
0,
2,
3,
};
Ref<ArrayMesh> mesh = memnew(ArrayMesh);
Array a;
a.resize(Mesh::ARRAY_MAX);
a[RS::ARRAY_VERTEX] = points;
a[RS::ARRAY_COLOR] = colors;
a[RS::ARRAY_INDEX] = indices;
mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, a);
return mesh;
}
void WorldBoundaryShape3D::_update_shape() {
PhysicsServer3D::get_singleton()->shape_set_data(get_shape(), plane);
Shape3D::_update_shape();

View file

@ -33,6 +33,8 @@
#include "scene/resources/3d/shape_3d.h"
class ArrayMesh;
class WorldBoundaryShape3D : public Shape3D {
GDCLASS(WorldBoundaryShape3D, Shape3D);
Plane plane;
@ -46,6 +48,7 @@ public:
const Plane &get_plane() const;
virtual Vector<Vector3> get_debug_mesh_lines() const override;
virtual Ref<ArrayMesh> get_debug_arraymesh_faces(const Color &p_modulate) const override;
virtual real_t get_enclosing_radius() const override {
// Should be infinite?
return 0;

View file

@ -1,4 +1,5 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
Import("env")
@ -6,7 +7,13 @@ Import("env")
thirdparty_obj = []
thirdparty_sources = "#thirdparty/misc/mikktspace.c"
thirdparty_dir = "#thirdparty/misc/"
thirdparty_sources = [
"mikktspace.c",
"qoa.c",
]
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
env_thirdparty = env.Clone()
env_thirdparty.disable_warnings()

View file

@ -174,7 +174,7 @@ float AnimatedTexture::get_speed_scale() const {
int AnimatedTexture::get_width() const {
RWLockRead r(rw_lock);
if (!frames[current_frame].texture.is_valid()) {
if (frames[current_frame].texture.is_null()) {
return 1;
}
@ -184,7 +184,7 @@ int AnimatedTexture::get_width() const {
int AnimatedTexture::get_height() const {
RWLockRead r(rw_lock);
if (!frames[current_frame].texture.is_valid()) {
if (frames[current_frame].texture.is_null()) {
return 1;
}
@ -198,7 +198,7 @@ RID AnimatedTexture::get_rid() const {
bool AnimatedTexture::has_alpha() const {
RWLockRead r(rw_lock);
if (!frames[current_frame].texture.is_valid()) {
if (frames[current_frame].texture.is_null()) {
return false;
}
@ -208,7 +208,7 @@ bool AnimatedTexture::has_alpha() const {
Ref<Image> AnimatedTexture::get_image() const {
RWLockRead r(rw_lock);
if (!frames[current_frame].texture.is_valid()) {
if (frames[current_frame].texture.is_null()) {
return Ref<Image>();
}

View file

@ -32,7 +32,6 @@
#include "animation.compat.inc"
#include "core/io/marshalls.h"
#include "core/math/geometry_3d.h"
bool Animation::_set(const StringName &p_name, const Variant &p_value) {
String prop_name = p_name;
@ -62,6 +61,23 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) {
compression.pages[i].time_offset = page["time_offset"];
}
compression.enabled = true;
return true;
} else if (prop_name == SNAME("markers")) {
Array markers = p_value;
for (const Dictionary marker : markers) {
ERR_FAIL_COND_V(!marker.has("name"), false);
ERR_FAIL_COND_V(!marker.has("time"), false);
StringName marker_name = marker["name"];
double time = marker["time"];
_marker_insert(time, marker_names, MarkerKey(time, marker_name));
marker_times.insert(marker_name, time);
Color color = Color(1, 1, 1);
if (marker.has("color")) {
color = marker["color"];
}
marker_colors.insert(marker_name, color);
}
return true;
} else if (prop_name.begins_with("tracks/")) {
int track = prop_name.get_slicec('/', 1).to_int();
@ -321,8 +337,12 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) {
Vector<real_t> times = d["times"];
Vector<real_t> values = d["points"];
#ifdef TOOLS_ENABLED
ERR_FAIL_COND_V(!d.has("handle_modes"), false);
Vector<int> handle_modes = d["handle_modes"];
Vector<int> handle_modes;
if (d.has("handle_modes")) {
handle_modes = d["handle_modes"];
} else {
handle_modes.resize_zeroed(times.size());
}
#endif // TOOLS_ENABLED
ERR_FAIL_COND_V(times.size() * 5 != values.size(), false);
@ -466,6 +486,18 @@ bool Animation::_get(const StringName &p_name, Variant &r_ret) const {
r_ret = comp;
return true;
} else if (prop_name == SNAME("markers")) {
Array markers;
for (HashMap<StringName, double>::ConstIterator E = marker_times.begin(); E; ++E) {
Dictionary d;
d["name"] = E->key;
d["time"] = E->value;
d["color"] = marker_colors[E->key];
markers.push_back(d);
}
r_ret = markers;
} else if (prop_name == "length") {
r_ret = length;
} else if (prop_name == "loop_mode") {
@ -835,6 +867,7 @@ void Animation::_get_property_list(List<PropertyInfo> *p_list) const {
if (compression.enabled) {
p_list->push_back(PropertyInfo(Variant::DICTIONARY, "_compression", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
}
p_list->push_back(PropertyInfo(Variant::ARRAY, "markers", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
for (int i = 0; i < tracks.size(); i++) {
p_list->push_back(PropertyInfo(Variant::STRING, "tracks/" + itos(i) + "/type", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
p_list->push_back(PropertyInfo(Variant::BOOL, "tracks/" + itos(i) + "/imported", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
@ -915,50 +948,50 @@ void Animation::remove_track(int p_track) {
case TYPE_POSITION_3D: {
PositionTrack *tt = static_cast<PositionTrack *>(t);
ERR_FAIL_COND_MSG(tt->compressed_track >= 0, "Compressed tracks can't be manually removed. Call clear() to get rid of compression first.");
_clear(tt->positions);
tt->positions.clear();
} break;
case TYPE_ROTATION_3D: {
RotationTrack *rt = static_cast<RotationTrack *>(t);
ERR_FAIL_COND_MSG(rt->compressed_track >= 0, "Compressed tracks can't be manually removed. Call clear() to get rid of compression first.");
_clear(rt->rotations);
rt->rotations.clear();
} break;
case TYPE_SCALE_3D: {
ScaleTrack *st = static_cast<ScaleTrack *>(t);
ERR_FAIL_COND_MSG(st->compressed_track >= 0, "Compressed tracks can't be manually removed. Call clear() to get rid of compression first.");
_clear(st->scales);
st->scales.clear();
} break;
case TYPE_BLEND_SHAPE: {
BlendShapeTrack *bst = static_cast<BlendShapeTrack *>(t);
ERR_FAIL_COND_MSG(bst->compressed_track >= 0, "Compressed tracks can't be manually removed. Call clear() to get rid of compression first.");
_clear(bst->blend_shapes);
bst->blend_shapes.clear();
} break;
case TYPE_VALUE: {
ValueTrack *vt = static_cast<ValueTrack *>(t);
_clear(vt->values);
vt->values.clear();
} break;
case TYPE_METHOD: {
MethodTrack *mt = static_cast<MethodTrack *>(t);
_clear(mt->methods);
mt->methods.clear();
} break;
case TYPE_BEZIER: {
BezierTrack *bz = static_cast<BezierTrack *>(t);
_clear(bz->values);
bz->values.clear();
} break;
case TYPE_AUDIO: {
AudioTrack *ad = static_cast<AudioTrack *>(t);
_clear(ad->values);
ad->values.clear();
} break;
case TYPE_ANIMATION: {
AnimationTrack *an = static_cast<AnimationTrack *>(t);
_clear(an->values);
an->values.clear();
} break;
}
@ -1014,7 +1047,7 @@ int Animation::find_track(const NodePath &p_path, const TrackType p_type) const
}
};
return -1;
};
}
Animation::TrackType Animation::get_cache_type(TrackType p_type) {
if (p_type == Animation::TYPE_BEZIER) {
@ -1083,9 +1116,25 @@ int Animation::_insert(double p_time, T &p_keys, const V &p_value) {
return -1;
}
template <typename T>
void Animation::_clear(T &p_keys) {
p_keys.clear();
int Animation::_marker_insert(double p_time, Vector<MarkerKey> &p_keys, const MarkerKey &p_value) {
int idx = p_keys.size();
while (true) {
// Condition for replacement.
if (idx > 0 && Math::is_equal_approx((double)p_keys[idx - 1].time, p_time)) {
p_keys.write[idx - 1] = p_value;
return idx - 1;
// Condition for insert.
} else if (idx == 0 || p_keys[idx - 1].time < p_time) {
p_keys.insert(idx, p_value);
return idx;
}
idx--;
}
return -1;
}
////
@ -1717,7 +1766,7 @@ int Animation::track_insert_key(int p_track, double p_time, const Variant &p_key
ERR_FAIL_COND_V(p_key.get_type() != Variant::DICTIONARY, -1);
Dictionary d = p_key;
ERR_FAIL_COND_V(!d.has("method") || (d["method"].get_type() != Variant::STRING_NAME && d["method"].get_type() != Variant::STRING), -1);
ERR_FAIL_COND_V(!d.has("method") || !d["method"].is_string(), -1);
ERR_FAIL_COND_V(!d.has("args") || !d["args"].is_array(), -1);
MethodKey k;
@ -3159,6 +3208,90 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl
}
}
void Animation::add_marker(const StringName &p_name, double p_time) {
int idx = _find(marker_names, p_time);
if (idx >= 0 && idx < marker_names.size() && Math::is_equal_approx(p_time, marker_names[idx].time)) {
marker_times.erase(marker_names[idx].name);
marker_colors.erase(marker_names[idx].name);
marker_names.write[idx].name = p_name;
marker_times.insert(p_name, p_time);
marker_colors.insert(p_name, Color(1, 1, 1));
} else {
_marker_insert(p_time, marker_names, MarkerKey(p_time, p_name));
marker_times.insert(p_name, p_time);
marker_colors.insert(p_name, Color(1, 1, 1));
}
}
void Animation::remove_marker(const StringName &p_name) {
HashMap<StringName, double>::Iterator E = marker_times.find(p_name);
ERR_FAIL_COND(!E);
int idx = _find(marker_names, E->value);
bool success = idx >= 0 && idx < marker_names.size() && Math::is_equal_approx(marker_names[idx].time, E->value);
ERR_FAIL_COND(!success);
marker_names.remove_at(idx);
marker_times.remove(E);
marker_colors.erase(p_name);
}
bool Animation::has_marker(const StringName &p_name) const {
return marker_times.has(p_name);
}
StringName Animation::get_marker_at_time(double p_time) const {
int idx = _find(marker_names, p_time);
if (idx >= 0 && idx < marker_names.size() && Math::is_equal_approx(marker_names[idx].time, p_time)) {
return marker_names[idx].name;
}
return StringName();
}
StringName Animation::get_next_marker(double p_time) const {
int idx = _find(marker_names, p_time);
if (idx >= -1 && idx < marker_names.size() - 1) {
// _find ensures that the time at idx is always the closest time to p_time that is also smaller to it.
// So we add 1 to get the next marker.
return marker_names[idx + 1].name;
}
return StringName();
}
StringName Animation::get_prev_marker(double p_time) const {
int idx = _find(marker_names, p_time);
if (idx >= 0 && idx < marker_names.size()) {
return marker_names[idx].name;
}
return StringName();
}
double Animation::get_marker_time(const StringName &p_name) const {
ERR_FAIL_COND_V(!marker_times.has(p_name), -1);
return marker_times.get(p_name);
}
PackedStringArray Animation::get_marker_names() const {
PackedStringArray names;
// We iterate on marker_names so the result is sorted by time.
for (const MarkerKey &marker_name : marker_names) {
names.push_back(marker_name.name);
}
return names;
}
Color Animation::get_marker_color(const StringName &p_name) const {
ERR_FAIL_COND_V(!marker_colors.has(p_name), Color());
return marker_colors[p_name];
}
void Animation::set_marker_color(const StringName &p_name, const Color &p_color) {
marker_colors[p_name] = p_color;
}
Vector<Variant> Animation::method_track_get_params(int p_track, int p_key_idx) const {
ERR_FAIL_INDEX_V(p_track, tracks.size(), Vector<Variant>());
Track *t = tracks[p_track];
@ -3890,6 +4023,17 @@ void Animation::_bind_methods() {
ClassDB::bind_method(D_METHOD("animation_track_set_key_animation", "track_idx", "key_idx", "animation"), &Animation::animation_track_set_key_animation);
ClassDB::bind_method(D_METHOD("animation_track_get_key_animation", "track_idx", "key_idx"), &Animation::animation_track_get_key_animation);
ClassDB::bind_method(D_METHOD("add_marker", "name", "time"), &Animation::add_marker);
ClassDB::bind_method(D_METHOD("remove_marker", "name"), &Animation::remove_marker);
ClassDB::bind_method(D_METHOD("has_marker", "name"), &Animation::has_marker);
ClassDB::bind_method(D_METHOD("get_marker_at_time", "time"), &Animation::get_marker_at_time);
ClassDB::bind_method(D_METHOD("get_next_marker", "time"), &Animation::get_next_marker);
ClassDB::bind_method(D_METHOD("get_prev_marker", "time"), &Animation::get_prev_marker);
ClassDB::bind_method(D_METHOD("get_marker_time", "name"), &Animation::get_marker_time);
ClassDB::bind_method(D_METHOD("get_marker_names"), &Animation::get_marker_names);
ClassDB::bind_method(D_METHOD("get_marker_color", "name"), &Animation::get_marker_color);
ClassDB::bind_method(D_METHOD("set_marker_color", "name", "color"), &Animation::set_marker_color);
ClassDB::bind_method(D_METHOD("set_length", "time_sec"), &Animation::set_length);
ClassDB::bind_method(D_METHOD("get_length"), &Animation::get_length);
@ -3902,6 +4046,7 @@ void Animation::_bind_methods() {
ClassDB::bind_method(D_METHOD("clear"), &Animation::clear);
ClassDB::bind_method(D_METHOD("copy_track", "track_idx", "to_animation"), &Animation::copy_track);
ClassDB::bind_method(D_METHOD("optimize", "allowed_velocity_err", "allowed_angular_err", "precision"), &Animation::optimize, DEFVAL(0.01), DEFVAL(0.01), DEFVAL(3));
ClassDB::bind_method(D_METHOD("compress", "page_size", "fps", "split_tolerance"), &Animation::compress, DEFVAL(8192), DEFVAL(120), DEFVAL(4.0));
ClassDB::bind_method(D_METHOD("is_capture_included"), &Animation::is_capture_included);
@ -4804,9 +4949,9 @@ void Animation::compress(uint32_t p_page_size, uint32_t p_fps, float p_split_tol
continue; // This track is exhausted (all keys were added already), don't consider.
}
}
uint32_t key_frame = double(track_get_key_time(uncomp_track, time_tracks[i].key_index)) / frame_len;
double key_time = track_get_key_time(uncomp_track, time_tracks[i].key_index);
double result = key_time / frame_len;
uint32_t key_frame = Math::fast_ftoi(result);
if (time_tracks[i].needs_start_frame && key_frame > base_page_frame) {
start_frame = true;
best_frame = base_page_frame;

View file

@ -45,7 +45,7 @@ public:
static inline String PARAMETERS_BASE_PATH = "parameters/";
enum TrackType {
enum TrackType : uint8_t {
TYPE_VALUE, // Set a value in a property, can be interpolated.
TYPE_POSITION_3D, // Position 3D track, can be compressed.
TYPE_ROTATION_3D, // Rotation 3D track, can be compressed.
@ -57,7 +57,7 @@ public:
TYPE_ANIMATION,
};
enum InterpolationType {
enum InterpolationType : uint8_t {
INTERPOLATION_NEAREST,
INTERPOLATION_LINEAR,
INTERPOLATION_CUBIC,
@ -65,26 +65,26 @@ public:
INTERPOLATION_CUBIC_ANGLE,
};
enum UpdateMode {
enum UpdateMode : uint8_t {
UPDATE_CONTINUOUS,
UPDATE_DISCRETE,
UPDATE_CAPTURE,
};
enum LoopMode {
enum LoopMode : uint8_t {
LOOP_NONE,
LOOP_LINEAR,
LOOP_PINGPONG,
};
// LoopedFlag is used in Animataion to "process the keys at both ends correct".
enum LoopedFlag {
enum LoopedFlag : uint8_t {
LOOPED_FLAG_NONE,
LOOPED_FLAG_END,
LOOPED_FLAG_START,
};
enum FindMode {
enum FindMode : uint8_t {
FIND_MODE_NEAREST,
FIND_MODE_APPROX,
FIND_MODE_EXACT,
@ -104,7 +104,6 @@ public:
};
#endif // TOOLS_ENABLED
private:
struct Track {
TrackType type = TrackType::TYPE_ANIMATION;
InterpolationType interpolation = INTERPOLATION_LINEAR;
@ -117,6 +116,7 @@ private:
virtual ~Track() {}
};
private:
struct Key {
real_t transition = 1.0;
double time = 0.0; // Time in secs.
@ -237,14 +237,27 @@ private:
}
};
Vector<Track *> tracks;
/* Marker */
template <typename T>
void _clear(T &p_keys);
struct MarkerKey {
double time;
StringName name;
MarkerKey(double p_time, const StringName &p_name) :
time(p_time), name(p_name) {}
MarkerKey() = default;
};
Vector<MarkerKey> marker_names; // time -> name
HashMap<StringName, double> marker_times; // name -> time
HashMap<StringName, Color> marker_colors; // name -> color
Vector<Track *> tracks;
template <typename T, typename V>
int _insert(double p_time, T &p_keys, const V &p_value);
int _marker_insert(double p_time, Vector<MarkerKey> &p_keys, const MarkerKey &p_value);
template <typename K>
inline int _find(const Vector<K> &p_keys, double p_time, bool p_backward = false, bool p_limit = false) const;
@ -361,8 +374,8 @@ private:
// bind helpers
private:
bool _float_track_optimize_key(const TKey<float> t0, const TKey<float> t1, const TKey<float> t2, real_t p_allowed_velocity_err, real_t p_allowed_precision_error);
bool _vector2_track_optimize_key(const TKey<Vector2> t0, const TKey<Vector2> t1, const TKey<Vector2> t2, real_t p_alowed_velocity_err, real_t p_allowed_angular_error, real_t p_allowed_precision_error);
bool _vector3_track_optimize_key(const TKey<Vector3> t0, const TKey<Vector3> t1, const TKey<Vector3> t2, real_t p_alowed_velocity_err, real_t p_allowed_angular_error, real_t p_allowed_precision_error);
bool _vector2_track_optimize_key(const TKey<Vector2> t0, const TKey<Vector2> t1, const TKey<Vector2> t2, real_t p_allowed_velocity_err, real_t p_allowed_angular_error, real_t p_allowed_precision_error);
bool _vector3_track_optimize_key(const TKey<Vector3> t0, const TKey<Vector3> t1, const TKey<Vector3> t2, real_t p_allowed_velocity_err, real_t p_allowed_angular_error, real_t p_allowed_precision_error);
bool _quaternion_track_optimize_key(const TKey<Quaternion> t0, const TKey<Quaternion> t1, const TKey<Quaternion> t2, real_t p_allowed_velocity_err, real_t p_allowed_angular_error, real_t p_allowed_precision_error);
void _position_track_optimize(int p_idx, real_t p_allowed_velocity_err, real_t p_allowed_angular_err, real_t p_allowed_precision_error);
@ -396,6 +409,10 @@ public:
int add_track(TrackType p_type, int p_at_pos = -1);
void remove_track(int p_track);
_FORCE_INLINE_ const Vector<Track *> get_tracks() {
return tracks;
}
bool is_capture_included() const;
int get_track_count() const;
@ -497,6 +514,17 @@ public:
void track_get_key_indices_in_range(int p_track, double p_time, double p_delta, List<int> *p_indices, Animation::LoopedFlag p_looped_flag = Animation::LOOPED_FLAG_NONE) const;
void add_marker(const StringName &p_name, double p_time);
void remove_marker(const StringName &p_name);
bool has_marker(const StringName &p_name) const;
StringName get_marker_at_time(double p_time) const;
StringName get_next_marker(double p_time) const;
StringName get_prev_marker(double p_time) const;
double get_marker_time(const StringName &p_time) const;
PackedStringArray get_marker_names() const;
Color get_marker_color(const StringName &p_name) const;
void set_marker_color(const StringName &p_name, const Color &p_color);
void set_length(real_t p_length);
real_t get_length() const;

View file

@ -33,11 +33,11 @@
#include "scene/scene_string_names.h"
bool AnimationLibrary::is_valid_animation_name(const String &p_name) {
return !(p_name.is_empty() || p_name.contains("/") || p_name.contains(":") || p_name.contains(",") || p_name.contains("["));
return !(p_name.is_empty() || p_name.contains_char('/') || p_name.contains_char(':') || p_name.contains_char(',') || p_name.contains_char('['));
}
bool AnimationLibrary::is_valid_library_name(const String &p_name) {
return !(p_name.contains("/") || p_name.contains(":") || p_name.contains(",") || p_name.contains("["));
return !(p_name.contains_char('/') || p_name.contains_char(':') || p_name.contains_char(',') || p_name.contains_char('['));
}
String AnimationLibrary::validate_library_name(const String &p_name) {
@ -125,6 +125,10 @@ void AnimationLibrary::get_animation_list(List<StringName> *p_animations) const
}
}
int AnimationLibrary::get_animation_list_size() const {
return animations.size();
}
void AnimationLibrary::_set_data(const Dictionary &p_data) {
for (KeyValue<StringName, Ref<Animation>> &K : animations) {
K.value->disconnect_changed(callable_mp(this, &AnimationLibrary::_animation_changed));
@ -166,6 +170,7 @@ void AnimationLibrary::_bind_methods() {
ClassDB::bind_method(D_METHOD("has_animation", "name"), &AnimationLibrary::has_animation);
ClassDB::bind_method(D_METHOD("get_animation", "name"), &AnimationLibrary::get_animation);
ClassDB::bind_method(D_METHOD("get_animation_list"), &AnimationLibrary::_get_animation_list);
ClassDB::bind_method(D_METHOD("get_animation_list_size"), &AnimationLibrary::get_animation_list_size);
ClassDB::bind_method(D_METHOD("_set_data", "data"), &AnimationLibrary::_set_data);
ClassDB::bind_method(D_METHOD("_get_data"), &AnimationLibrary::_get_data);

View file

@ -61,6 +61,7 @@ public:
bool has_animation(const StringName &p_name) const;
Ref<Animation> get_animation(const StringName &p_name) const;
void get_animation_list(List<StringName> *p_animations) const;
int get_animation_list_size() const;
#ifdef TOOLS_ENABLED
virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override;

View file

@ -190,7 +190,7 @@ void AtlasTexture::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, cons
}
bool AtlasTexture::get_rect_region(const Rect2 &p_rect, const Rect2 &p_src_rect, Rect2 &r_rect, Rect2 &r_src_rect) const {
if (!atlas.is_valid()) {
if (atlas.is_null()) {
return false;
}

View file

@ -31,7 +31,6 @@
#include "audio_stream_polyphonic.h"
#include "audio_stream_polyphonic.compat.inc"
#include "scene/main/scene_tree.h"
#include "servers/audio_server.h"
constexpr uint64_t ID_MASK = 0xFFFFFFFF;
@ -247,6 +246,11 @@ AudioStreamPlaybackPolyphonic::ID AudioStreamPlaybackPolyphonic::play_stream(con
sp->volume_vector.write[2] = AudioFrame(linear_volume, linear_volume);
sp->volume_vector.write[3] = AudioFrame(linear_volume, linear_volume);
sp->bus = p_bus;
if (streams[i].stream_playback->get_sample_playback().is_valid()) {
AudioServer::get_singleton()->stop_playback_stream(sp);
}
streams[i].stream_playback->set_sample_playback(sp);
AudioServer::get_singleton()->start_sample_playback(sp);
}
@ -273,6 +277,21 @@ AudioStreamPlaybackPolyphonic::Stream *AudioStreamPlaybackPolyphonic::_find_stre
return &streams[index];
}
const AudioStreamPlaybackPolyphonic::Stream *AudioStreamPlaybackPolyphonic::_find_stream(int64_t p_id) const {
uint32_t index = static_cast<uint64_t>(p_id) >> INDEX_SHIFT;
if (index >= streams.size()) {
return nullptr;
}
if (!streams[index].active.is_set()) {
return nullptr; // Not active, no longer exists.
}
int64_t id = static_cast<uint64_t>(p_id) & ID_MASK;
if (streams[index].id != id) {
return nullptr;
}
return &streams[index];
}
void AudioStreamPlaybackPolyphonic::set_stream_volume(ID p_stream_id, float p_volume_db) {
Stream *s = _find_stream(p_stream_id);
if (!s) {
@ -290,7 +309,7 @@ void AudioStreamPlaybackPolyphonic::set_stream_pitch_scale(ID p_stream_id, float
}
bool AudioStreamPlaybackPolyphonic::is_stream_playing(ID p_stream_id) const {
return const_cast<AudioStreamPlaybackPolyphonic *>(this)->_find_stream(p_stream_id) != nullptr;
return _find_stream(p_stream_id) != nullptr;
}
void AudioStreamPlaybackPolyphonic::stop_stream(ID p_stream_id) {
@ -315,6 +334,9 @@ Ref<AudioSamplePlayback> AudioStreamPlaybackPolyphonic::get_sample_playback() co
void AudioStreamPlaybackPolyphonic::set_sample_playback(const Ref<AudioSamplePlayback> &p_playback) {
sample_playback = p_playback;
if (sample_playback.is_valid()) {
sample_playback->stream_playback = Ref<AudioStreamPlayback>(this);
}
}
void AudioStreamPlaybackPolyphonic::_bind_methods() {

View file

@ -88,6 +88,7 @@ class AudioStreamPlaybackPolyphonic : public AudioStreamPlayback {
Ref<AudioSamplePlayback> sample_playback;
_FORCE_INLINE_ Stream *_find_stream(int64_t p_id);
_FORCE_INLINE_ const Stream *_find_stream(int64_t p_id) const;
friend class AudioStreamPolyphonic;

View file

@ -30,9 +30,12 @@
#include "audio_stream_wav.h"
#include "core/io/file_access.h"
#include "core/io/file_access_memory.h"
#include "core/io/marshalls.h"
const float TRIM_DB_LIMIT = -50;
const int TRIM_FADE_OUT_FRAMES = 500;
void AudioStreamPlaybackWAV::start(double p_from_pos) {
if (base->format == AudioStreamWAV::FORMAT_IMA_ADPCM) {
//no seeking in IMA_ADPCM
@ -123,10 +126,8 @@ void AudioStreamPlaybackWAV::do_resample(const Depth *p_src, AudioFrame *p_dst,
int16_t nibble, diff, step;
p_ima_adpcm[i].last_nibble++;
const uint8_t *src_ptr = (const uint8_t *)base->data;
src_ptr += AudioStreamWAV::DATA_PAD;
uint8_t nbb = src_ptr[(p_ima_adpcm[i].last_nibble >> 1) * (is_stereo ? 2 : 1) + i];
uint8_t nbb = p_src[(p_ima_adpcm[i].last_nibble >> 1) * (is_stereo ? 2 : 1) + i];
nibble = (p_ima_adpcm[i].last_nibble & 1) ? (nbb >> 4) : (nbb & 0xF);
step = _ima_adpcm_step_table[p_ima_adpcm[i].step_index];
@ -179,17 +180,16 @@ void AudioStreamPlaybackWAV::do_resample(const Depth *p_src, AudioFrame *p_dst,
if (pos != p_qoa->cache_pos) { // Prevents triple decoding on lower mix rates.
for (int i = 0; i < 2; i++) {
// Sign operations prevent triple decoding on backward loops, maxing prevents pop.
uint32_t interp_pos = MIN(pos + (i * sign) + (sign < 0), p_qoa->desc->samples - 1);
uint32_t interp_pos = MIN(pos + (i * sign) + (sign < 0), p_qoa->desc.samples - 1);
uint32_t new_data_ofs = 8 + interp_pos / QOA_FRAME_LEN * p_qoa->frame_len;
if (p_qoa->data_ofs != new_data_ofs) {
p_qoa->data_ofs = new_data_ofs;
const uint8_t *src_ptr = (const uint8_t *)base->data;
src_ptr += p_qoa->data_ofs + AudioStreamWAV::DATA_PAD;
qoa_decode_frame(src_ptr, p_qoa->frame_len, p_qoa->desc, p_qoa->dec, &p_qoa->dec_len);
const uint8_t *ofs_src = (uint8_t *)p_src + p_qoa->data_ofs;
qoa_decode_frame(ofs_src, p_qoa->frame_len, &p_qoa->desc, p_qoa->dec.ptr(), &p_qoa->dec_len);
}
uint32_t dec_idx = (interp_pos % QOA_FRAME_LEN) * p_qoa->desc->channels;
uint32_t dec_idx = (interp_pos % QOA_FRAME_LEN) * p_qoa->desc.channels;
if ((sign > 0 && i == 0) || (sign < 0 && i == 1)) {
final = p_qoa->dec[dec_idx];
@ -267,7 +267,7 @@ void AudioStreamPlaybackWAV::do_resample(const Depth *p_src, AudioFrame *p_dst,
}
int AudioStreamPlaybackWAV::mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) {
if (!base->data || !active) {
if (base->data.is_empty() || !active) {
for (int i = 0; i < p_frames; i++) {
p_buffer[i] = AudioFrame(0, 0);
}
@ -286,7 +286,7 @@ int AudioStreamPlaybackWAV::mix(AudioFrame *p_buffer, float p_rate_scale, int p_
len *= 2;
break;
case AudioStreamWAV::FORMAT_QOA:
len = qoa.desc->samples * qoa.desc->channels;
len = qoa.desc.samples * qoa.desc.channels;
break;
}
@ -300,7 +300,7 @@ int AudioStreamPlaybackWAV::mix(AudioFrame *p_buffer, float p_rate_scale, int p_
int64_t loop_end_fp = ((int64_t)base->loop_end << MIX_FRAC_BITS);
int64_t length_fp = ((int64_t)len << MIX_FRAC_BITS);
int64_t begin_limit = (base->loop_mode != AudioStreamWAV::LOOP_DISABLED) ? loop_begin_fp : 0;
int64_t end_limit = (base->loop_mode != AudioStreamWAV::LOOP_DISABLED) ? loop_end_fp : length_fp;
int64_t end_limit = (base->loop_mode != AudioStreamWAV::LOOP_DISABLED) ? loop_end_fp : length_fp - MIX_FRAC_LEN;
bool is_stereo = base->stereo;
int32_t todo = p_frames;
@ -324,8 +324,7 @@ int AudioStreamPlaybackWAV::mix(AudioFrame *p_buffer, float p_rate_scale, int p_
/* audio data */
uint8_t *dataptr = (uint8_t *)base->data;
const void *data = dataptr + AudioStreamWAV::DATA_PAD;
const uint8_t *data = base->data.ptr() + AudioStreamWAV::DATA_PAD;
AudioFrame *dst_buff = p_buffer;
if (format == AudioStreamWAV::FORMAT_IMA_ADPCM) {
@ -479,19 +478,14 @@ Ref<AudioSamplePlayback> AudioStreamPlaybackWAV::get_sample_playback() const {
void AudioStreamPlaybackWAV::set_sample_playback(const Ref<AudioSamplePlayback> &p_playback) {
sample_playback = p_playback;
if (sample_playback.is_valid()) {
sample_playback->stream_playback = Ref<AudioStreamPlayback>(this);
}
}
AudioStreamPlaybackWAV::AudioStreamPlaybackWAV() {}
AudioStreamPlaybackWAV::~AudioStreamPlaybackWAV() {
if (qoa.desc) {
memfree(qoa.desc);
}
if (qoa.dec) {
memfree(qoa.dec);
}
}
AudioStreamPlaybackWAV::~AudioStreamPlaybackWAV() {}
/////////////////////
@ -557,9 +551,10 @@ double AudioStreamWAV::get_length() const {
len *= 2;
break;
case AudioStreamWAV::FORMAT_QOA:
qoa_desc desc = { 0, 0, 0, { { { 0 }, { 0 } } } };
qoa_decode_header((uint8_t *)data + DATA_PAD, data_bytes, &desc);
qoa_desc desc = {};
qoa_decode_header(data.ptr() + DATA_PAD, data_bytes, &desc);
len = desc.samples * desc.channels;
break;
}
if (stereo) {
@ -575,22 +570,16 @@ bool AudioStreamWAV::is_monophonic() const {
void AudioStreamWAV::set_data(const Vector<uint8_t> &p_data) {
AudioServer::get_singleton()->lock();
if (data) {
memfree(data);
data = nullptr;
data_bytes = 0;
}
int datalen = p_data.size();
if (datalen) {
const uint8_t *r = p_data.ptr();
int alloc_len = datalen + DATA_PAD * 2;
data = memalloc(alloc_len); //alloc with some padding for interpolation
memset(data, 0, alloc_len);
uint8_t *dataptr = (uint8_t *)data;
memcpy(dataptr + DATA_PAD, r, datalen);
data_bytes = datalen;
}
int src_data_len = p_data.size();
data.clear();
int alloc_len = src_data_len + DATA_PAD * 2;
data.resize(alloc_len);
memset(data.ptr(), 0, alloc_len);
memcpy(data.ptr() + DATA_PAD, p_data.ptr(), src_data_len);
data_bytes = src_data_len;
AudioServer::get_singleton()->unlock();
}
@ -598,13 +587,9 @@ void AudioStreamWAV::set_data(const Vector<uint8_t> &p_data) {
Vector<uint8_t> AudioStreamWAV::get_data() const {
Vector<uint8_t> pv;
if (data) {
if (data_bytes) {
pv.resize(data_bytes);
{
uint8_t *w = pv.ptrw();
uint8_t *dataptr = (uint8_t *)data;
memcpy(w, dataptr + DATA_PAD, data_bytes);
}
memcpy(pv.ptrw(), data.ptr() + DATA_PAD, data_bytes);
}
return pv;
@ -642,7 +627,7 @@ Error AudioStreamWAV::save_to_wav(const String &p_path) {
}
String file_path = p_path;
if (!(file_path.substr(file_path.length() - 4, 4) == ".wav")) {
if (file_path.substr(file_path.length() - 4, 4).to_lower() != ".wav") {
file_path += ".wav";
}
@ -696,13 +681,12 @@ Ref<AudioStreamPlayback> AudioStreamWAV::instantiate_playback() {
sample->base = Ref<AudioStreamWAV>(this);
if (format == AudioStreamWAV::FORMAT_QOA) {
sample->qoa.desc = (qoa_desc *)memalloc(sizeof(qoa_desc));
uint32_t ffp = qoa_decode_header((uint8_t *)data + DATA_PAD, data_bytes, sample->qoa.desc);
uint32_t ffp = qoa_decode_header(data.ptr() + DATA_PAD, data_bytes, &sample->qoa.desc);
ERR_FAIL_COND_V(ffp != 8, Ref<AudioStreamPlaybackWAV>());
sample->qoa.frame_len = qoa_max_frame_size(sample->qoa.desc);
int samples_len = (sample->qoa.desc->samples > QOA_FRAME_LEN ? QOA_FRAME_LEN : sample->qoa.desc->samples);
int alloc_len = sample->qoa.desc->channels * samples_len * sizeof(int16_t);
sample->qoa.dec = (int16_t *)memalloc(alloc_len);
sample->qoa.frame_len = qoa_max_frame_size(&sample->qoa.desc);
int samples_len = (sample->qoa.desc.samples > QOA_FRAME_LEN ? QOA_FRAME_LEN : sample->qoa.desc.samples);
int dec_len = sample->qoa.desc.channels * samples_len;
sample->qoa.dec.resize(dec_len);
}
return sample;
@ -739,7 +723,469 @@ Ref<AudioSample> AudioStreamWAV::generate_sample() const {
return sample;
}
Ref<AudioStreamWAV> AudioStreamWAV::load_from_buffer(const Vector<uint8_t> &p_stream_data, const Dictionary &p_options) {
// /* STEP 1, READ WAVE FILE */
Ref<FileAccessMemory> file;
file.instantiate();
Error err = file->open_custom(p_stream_data.ptr(), p_stream_data.size());
ERR_FAIL_COND_V_MSG(err != OK, Ref<AudioStreamWAV>(), "Cannot create memfile for WAV file buffer.");
/* CHECK RIFF */
char riff[5];
riff[4] = 0;
file->get_buffer((uint8_t *)&riff, 4); //RIFF
if (riff[0] != 'R' || riff[1] != 'I' || riff[2] != 'F' || riff[3] != 'F') {
ERR_FAIL_V_MSG(Ref<AudioStreamWAV>(), vformat("Not a WAV file. File should start with 'RIFF', but found '%s', in file of size %d bytes", riff, file->get_length()));
}
/* GET FILESIZE */
// The file size in header is 8 bytes less than the actual size.
// See https://docs.fileformat.com/audio/wav/
const int FILE_SIZE_HEADER_OFFSET = 8;
uint32_t file_size_header = file->get_32() + FILE_SIZE_HEADER_OFFSET;
uint64_t file_size = file->get_length();
if (file_size != file_size_header) {
WARN_PRINT(vformat("File size %d is %s than the expected size %d.", file_size, file_size > file_size_header ? "larger" : "smaller", file_size_header));
}
/* CHECK WAVE */
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') {
ERR_FAIL_V_MSG(Ref<AudioStreamWAV>(), vformat("Not a WAV file. Header should contain 'WAVE', but found '%s', in file of size %d bytes", wave, file->get_length()));
}
// Let users override potential loop points from the WAV.
// We parse the WAV loop points only with "Detect From WAV" (0).
int import_loop_mode = p_options["edit/loop_mode"];
int format_bits = 0;
int format_channels = 0;
AudioStreamWAV::LoopMode loop_mode = AudioStreamWAV::LOOP_DISABLED;
uint16_t compression_code = 1;
bool format_found = false;
bool data_found = false;
int format_freq = 0;
int loop_begin = 0;
int loop_end = 0;
int frames = 0;
Vector<float> data;
while (!file->eof_reached()) {
/* chunk */
char chunk_id[4];
file->get_buffer((uint8_t *)&chunk_id, 4); //RIFF
/* chunk size */
uint32_t chunksize = file->get_32();
uint32_t file_pos = file->get_position(); //save file pos, so we can skip to next chunk safely
if (file->eof_reached()) {
//ERR_PRINT("EOF REACH");
break;
}
if (chunk_id[0] == 'f' && chunk_id[1] == 'm' && chunk_id[2] == 't' && chunk_id[3] == ' ' && !format_found) {
/* IS FORMAT CHUNK */
//Issue: #7755 : Not a bug - usage of other formats (format codes) are unsupported in current importer version.
//Consider revision for engine version 3.0
compression_code = file->get_16();
if (compression_code != 1 && compression_code != 3) {
ERR_FAIL_V_MSG(Ref<AudioStreamWAV>(), "Format not supported for WAVE file (not PCM). Save WAVE files as uncompressed PCM or IEEE float instead.");
}
format_channels = file->get_16();
if (format_channels != 1 && format_channels != 2) {
ERR_FAIL_V_MSG(Ref<AudioStreamWAV>(), "Format not supported for WAVE file (not stereo or mono).");
}
format_freq = file->get_32(); //sampling rate
file->get_32(); // average bits/second (unused)
file->get_16(); // block align (unused)
format_bits = file->get_16(); // bits per sample
if (format_bits % 8 || format_bits == 0) {
ERR_FAIL_V_MSG(Ref<AudioStreamWAV>(), "Invalid amount of bits in the sample (should be one of 8, 16, 24 or 32).");
}
if (compression_code == 3 && format_bits % 32) {
ERR_FAIL_V_MSG(Ref<AudioStreamWAV>(), "Invalid amount of bits in the IEEE float sample (should be 32 or 64).");
}
/* Don't need anything else, continue */
format_found = true;
}
if (chunk_id[0] == 'd' && chunk_id[1] == 'a' && chunk_id[2] == 't' && chunk_id[3] == 'a' && !data_found) {
/* IS DATA CHUNK */
data_found = true;
if (!format_found) {
ERR_PRINT("'data' chunk before 'format' chunk found.");
break;
}
uint64_t remaining_bytes = file_size - file_pos;
frames = chunksize;
if (remaining_bytes < chunksize) {
WARN_PRINT("Data chunk size is smaller than expected. Proceeding with actual data size.");
frames = remaining_bytes;
}
ERR_FAIL_COND_V(format_channels == 0, Ref<AudioStreamWAV>());
frames /= format_channels;
frames /= (format_bits >> 3);
/*print_line("chunksize: "+itos(chunksize));
print_line("channels: "+itos(format_channels));
print_line("bits: "+itos(format_bits));
*/
data.resize(frames * format_channels);
if (compression_code == 1) {
if (format_bits == 8) {
for (int i = 0; i < frames * format_channels; i++) {
// 8 bit samples are UNSIGNED
data.write[i] = int8_t(file->get_8() - 128) / 128.f;
}
} else if (format_bits == 16) {
for (int i = 0; i < frames * format_channels; i++) {
//16 bit SIGNED
data.write[i] = int16_t(file->get_16()) / 32768.f;
}
} else {
for (int i = 0; i < frames * format_channels; i++) {
//16+ bits samples are SIGNED
// if sample is > 16 bits, just read extra bytes
uint32_t s = 0;
for (int b = 0; b < (format_bits >> 3); b++) {
s |= ((uint32_t)file->get_8()) << (b * 8);
}
s <<= (32 - format_bits);
data.write[i] = (int32_t(s) >> 16) / 32768.f;
}
}
} else if (compression_code == 3) {
if (format_bits == 32) {
for (int i = 0; i < frames * format_channels; i++) {
//32 bit IEEE Float
data.write[i] = file->get_float();
}
} else if (format_bits == 64) {
for (int i = 0; i < frames * format_channels; i++) {
//64 bit IEEE Float
data.write[i] = file->get_double();
}
}
}
// This is commented out due to some weird edge case seemingly in FileAccessMemory, doesn't seem to have any side effects though.
// if (file->eof_reached()) {
// ERR_FAIL_V_MSG(Ref<AudioStreamWAV>(), "Premature end of file.");
// }
}
if (import_loop_mode == 0 && chunk_id[0] == 's' && chunk_id[1] == 'm' && chunk_id[2] == 'p' && chunk_id[3] == 'l') {
// Loop point info!
/**
* Consider exploring next document:
* http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/Docs/RIFFNEW.pdf
* Especially on page:
* 16 - 17
* Timestamp:
* 22:38 06.07.2017 GMT
**/
for (int i = 0; i < 10; i++) {
file->get_32(); // i wish to know why should i do this... no doc!
}
// only read 0x00 (loop forward), 0x01 (loop ping-pong) and 0x02 (loop backward)
// Skip anything else because it's not supported, reserved for future uses or sampler specific
// from https://sites.google.com/site/musicgapi/technical-documents/wav-file-format#smpl (loop type values table)
int loop_type = file->get_32();
if (loop_type == 0x00 || loop_type == 0x01 || loop_type == 0x02) {
if (loop_type == 0x00) {
loop_mode = AudioStreamWAV::LOOP_FORWARD;
} else if (loop_type == 0x01) {
loop_mode = AudioStreamWAV::LOOP_PINGPONG;
} else if (loop_type == 0x02) {
loop_mode = AudioStreamWAV::LOOP_BACKWARD;
}
loop_begin = file->get_32();
loop_end = file->get_32();
}
}
// Move to the start of the next chunk. Note that RIFF requires a padding byte for odd
// chunk sizes.
file->seek(file_pos + chunksize + (chunksize & 1));
}
// STEP 2, APPLY CONVERSIONS
bool is16 = format_bits != 8;
int rate = format_freq;
/*
print_line("Input Sample: ");
print_line("\tframes: " + itos(frames));
print_line("\tformat_channels: " + itos(format_channels));
print_line("\t16bits: " + itos(is16));
print_line("\trate: " + itos(rate));
print_line("\tloop: " + itos(loop));
print_line("\tloop begin: " + itos(loop_begin));
print_line("\tloop end: " + itos(loop_end));
*/
//apply frequency limit
bool limit_rate = p_options["force/max_rate"];
int limit_rate_hz = p_options["force/max_rate_hz"];
if (limit_rate && rate > limit_rate_hz && rate > 0 && frames > 0) {
// resample!
int new_data_frames = (int)(frames * (float)limit_rate_hz / (float)rate);
Vector<float> new_data;
new_data.resize(new_data_frames * format_channels);
for (int c = 0; c < format_channels; c++) {
float frac = 0.0;
int ipos = 0;
for (int i = 0; i < new_data_frames; i++) {
// Cubic interpolation should be enough.
float y0 = data[MAX(0, ipos - 1) * format_channels + c];
float y1 = data[ipos * format_channels + c];
float y2 = data[MIN(frames - 1, ipos + 1) * format_channels + c];
float y3 = data[MIN(frames - 1, ipos + 2) * format_channels + c];
new_data.write[i * format_channels + c] = Math::cubic_interpolate(y1, y2, y0, y3, frac);
// update position and always keep fractional part within ]0...1]
// in order to avoid 32bit floating point precision errors
frac += (float)rate / (float)limit_rate_hz;
int tpos = (int)Math::floor(frac);
ipos += tpos;
frac -= tpos;
}
}
if (loop_mode) {
loop_begin = (int)(loop_begin * (float)new_data_frames / (float)frames);
loop_end = (int)(loop_end * (float)new_data_frames / (float)frames);
}
data = new_data;
rate = limit_rate_hz;
frames = new_data_frames;
}
bool normalize = p_options["edit/normalize"];
if (normalize) {
float max = 0.0;
for (int i = 0; i < data.size(); i++) {
float amp = Math::abs(data[i]);
if (amp > max) {
max = amp;
}
}
if (max > 0) {
float mult = 1.0 / max;
for (int i = 0; i < data.size(); i++) {
data.write[i] *= mult;
}
}
}
bool trim = p_options["edit/trim"];
if (trim && (loop_mode == AudioStreamWAV::LOOP_DISABLED) && format_channels > 0) {
int first = 0;
int last = (frames / format_channels) - 1;
bool found = false;
float limit = Math::db_to_linear(TRIM_DB_LIMIT);
for (int i = 0; i < data.size() / format_channels; i++) {
float amp_channel_sum = 0.0;
for (int j = 0; j < format_channels; j++) {
amp_channel_sum += Math::abs(data[(i * format_channels) + j]);
}
float amp = Math::abs(amp_channel_sum / (float)format_channels);
if (!found && amp > limit) {
first = i;
found = true;
}
if (found && amp > limit) {
last = i;
}
}
if (first < last) {
Vector<float> new_data;
new_data.resize((last - first) * format_channels);
for (int i = first; i < last; i++) {
float fade_out_mult = 1.0;
if (last - i < TRIM_FADE_OUT_FRAMES) {
fade_out_mult = ((float)(last - i - 1) / (float)TRIM_FADE_OUT_FRAMES);
}
for (int j = 0; j < format_channels; j++) {
new_data.write[((i - first) * format_channels) + j] = data[(i * format_channels) + j] * fade_out_mult;
}
}
data = new_data;
frames = data.size() / format_channels;
}
}
if (import_loop_mode >= 2) {
loop_mode = (AudioStreamWAV::LoopMode)(import_loop_mode - 1);
loop_begin = p_options["edit/loop_begin"];
loop_end = p_options["edit/loop_end"];
// Wrap around to max frames, so `-1` can be used to select the end, etc.
if (loop_begin < 0) {
loop_begin = CLAMP(loop_begin + frames, 0, frames - 1);
}
if (loop_end < 0) {
loop_end = CLAMP(loop_end + frames, 0, frames - 1);
}
}
int compression = p_options["compress/mode"];
bool force_mono = p_options["force/mono"];
if (force_mono && format_channels == 2) {
Vector<float> new_data;
new_data.resize(data.size() / 2);
for (int i = 0; i < frames; i++) {
new_data.write[i] = (data[i * 2 + 0] + data[i * 2 + 1]) / 2.0;
}
data = new_data;
format_channels = 1;
}
bool force_8_bit = p_options["force/8_bit"];
if (force_8_bit) {
is16 = false;
}
Vector<uint8_t> dst_data;
AudioStreamWAV::Format dst_format;
if (compression == 1) {
dst_format = AudioStreamWAV::FORMAT_IMA_ADPCM;
if (format_channels == 1) {
_compress_ima_adpcm(data, dst_data);
} else {
//byte interleave
Vector<float> left;
Vector<float> right;
int tframes = data.size() / 2;
left.resize(tframes);
right.resize(tframes);
for (int i = 0; i < tframes; i++) {
left.write[i] = data[i * 2 + 0];
right.write[i] = data[i * 2 + 1];
}
Vector<uint8_t> bleft;
Vector<uint8_t> bright;
_compress_ima_adpcm(left, bleft);
_compress_ima_adpcm(right, bright);
int dl = bleft.size();
dst_data.resize(dl * 2);
uint8_t *w = dst_data.ptrw();
const uint8_t *rl = bleft.ptr();
const uint8_t *rr = bright.ptr();
for (int i = 0; i < dl; i++) {
w[i * 2 + 0] = rl[i];
w[i * 2 + 1] = rr[i];
}
}
} else if (compression == 2) {
dst_format = AudioStreamWAV::FORMAT_QOA;
qoa_desc desc = {};
desc.samplerate = rate;
desc.samples = frames;
desc.channels = format_channels;
_compress_qoa(data, dst_data, &desc);
} else {
dst_format = is16 ? AudioStreamWAV::FORMAT_16_BITS : AudioStreamWAV::FORMAT_8_BITS;
dst_data.resize(data.size() * (is16 ? 2 : 1));
{
uint8_t *w = dst_data.ptrw();
int ds = data.size();
for (int i = 0; i < ds; i++) {
if (is16) {
int16_t v = CLAMP(data[i] * 32768, -32768, 32767);
encode_uint16(v, &w[i * 2]);
} else {
int8_t v = CLAMP(data[i] * 128, -128, 127);
w[i] = v;
}
}
}
}
Ref<AudioStreamWAV> sample;
sample.instantiate();
sample->set_data(dst_data);
sample->set_format(dst_format);
sample->set_mix_rate(rate);
sample->set_loop_mode(loop_mode);
sample->set_loop_begin(loop_begin);
sample->set_loop_end(loop_end);
sample->set_stereo(format_channels == 2);
return sample;
}
Ref<AudioStreamWAV> AudioStreamWAV::load_from_file(const String &p_path, const Dictionary &p_options) {
const Vector<uint8_t> stream_data = FileAccess::get_file_as_bytes(p_path);
ERR_FAIL_COND_V_MSG(stream_data.is_empty(), Ref<AudioStreamWAV>(), vformat("Cannot open file '%s'.", p_path));
return load_from_buffer(stream_data, p_options);
}
void AudioStreamWAV::_bind_methods() {
ClassDB::bind_static_method("AudioStreamWAV", D_METHOD("load_from_buffer", "stream_data", "options"), &AudioStreamWAV::load_from_buffer, DEFVAL(Dictionary()));
ClassDB::bind_static_method("AudioStreamWAV", D_METHOD("load_from_file", "path", "options"), &AudioStreamWAV::load_from_file, DEFVAL(Dictionary()));
ClassDB::bind_method(D_METHOD("set_data", "data"), &AudioStreamWAV::set_data);
ClassDB::bind_method(D_METHOD("get_data"), &AudioStreamWAV::get_data);
@ -764,7 +1210,7 @@ void AudioStreamWAV::_bind_methods() {
ClassDB::bind_method(D_METHOD("save_to_wav", "path"), &AudioStreamWAV::save_to_wav);
ADD_PROPERTY(PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_data", "get_data");
ADD_PROPERTY(PropertyInfo(Variant::INT, "format", PROPERTY_HINT_ENUM, "8-Bit,16-Bit,IMA-ADPCM,QOA"), "set_format", "get_format");
ADD_PROPERTY(PropertyInfo(Variant::INT, "format", PROPERTY_HINT_ENUM, "8-Bit,16-Bit,IMA ADPCM,Quite OK Audio"), "set_format", "get_format");
ADD_PROPERTY(PropertyInfo(Variant::INT, "loop_mode", PROPERTY_HINT_ENUM, "Disabled,Forward,Ping-Pong,Backward"), "set_loop_mode", "get_loop_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "loop_begin"), "set_loop_begin", "get_loop_begin");
ADD_PROPERTY(PropertyInfo(Variant::INT, "loop_end"), "set_loop_end", "get_loop_end");
@ -784,10 +1230,4 @@ void AudioStreamWAV::_bind_methods() {
AudioStreamWAV::AudioStreamWAV() {}
AudioStreamWAV::~AudioStreamWAV() {
if (data) {
memfree(data);
data = nullptr;
data_bytes = 0;
}
}
AudioStreamWAV::~AudioStreamWAV() {}

View file

@ -31,10 +31,8 @@
#ifndef AUDIO_STREAM_WAV_H
#define AUDIO_STREAM_WAV_H
#define QOA_IMPLEMENTATION
#define QOA_NO_STDIO
#include "servers/audio/audio_stream.h"
#include "thirdparty/misc/qoa.h"
class AudioStreamWAV;
@ -59,10 +57,10 @@ class AudioStreamPlaybackWAV : public AudioStreamPlayback {
} ima_adpcm[2];
struct QOA_State {
qoa_desc *desc = nullptr;
qoa_desc desc = {};
uint32_t data_ofs = 0;
uint32_t frame_len = 0;
int16_t *dec = nullptr;
LocalVector<int16_t> dec;
uint32_t dec_len = 0;
int64_t cache_pos = -1;
int16_t cache[2] = { 0, 0 };
@ -137,13 +135,16 @@ private:
int loop_begin = 0;
int loop_end = 0;
int mix_rate = 44100;
void *data = nullptr;
LocalVector<uint8_t> data;
uint32_t data_bytes = 0;
protected:
static void _bind_methods();
public:
static Ref<AudioStreamWAV> load_from_buffer(const Vector<uint8_t> &p_stream_data, const Dictionary &p_options);
static Ref<AudioStreamWAV> load_from_file(const String &p_path, const Dictionary &p_options);
void set_format(Format p_format);
Format get_format() const;
@ -179,6 +180,125 @@ public:
}
virtual Ref<AudioSample> generate_sample() const override;
static void _compress_ima_adpcm(const Vector<float> &p_data, Vector<uint8_t> &r_dst_data) {
static const int16_t _ima_adpcm_step_table[89] = {
7, 8, 9, 10, 11, 12, 13, 14, 16, 17,
19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
50, 55, 60, 66, 73, 80, 88, 97, 107, 118,
130, 143, 157, 173, 190, 209, 230, 253, 279, 307,
337, 371, 408, 449, 494, 544, 598, 658, 724, 796,
876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066,
2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,
5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
};
static const int8_t _ima_adpcm_index_table[16] = {
-1, -1, -1, -1, 2, 4, 6, 8,
-1, -1, -1, -1, 2, 4, 6, 8
};
int datalen = p_data.size();
int datamax = datalen;
if (datalen & 1) {
datalen++;
}
r_dst_data.resize(datalen / 2 + 4);
uint8_t *w = r_dst_data.ptrw();
int i, step_idx = 0, prev = 0;
uint8_t *out = w;
const float *in = p_data.ptr();
// Initial value is zero.
*(out++) = 0;
*(out++) = 0;
// Table index initial value.
*(out++) = 0;
// Unused.
*(out++) = 0;
for (i = 0; i < datalen; i++) {
int step, diff, vpdiff, mask;
uint8_t nibble;
int16_t xm_sample;
if (i >= datamax) {
xm_sample = 0;
} else {
xm_sample = CLAMP(in[i] * 32767.0, -32768, 32767);
}
diff = (int)xm_sample - prev;
nibble = 0;
step = _ima_adpcm_step_table[step_idx];
vpdiff = step >> 3;
if (diff < 0) {
nibble = 8;
diff = -diff;
}
mask = 4;
while (mask) {
if (diff >= step) {
nibble |= mask;
diff -= step;
vpdiff += step;
}
step >>= 1;
mask >>= 1;
}
if (nibble & 8) {
prev -= vpdiff;
} else {
prev += vpdiff;
}
prev = CLAMP(prev, -32768, 32767);
step_idx += _ima_adpcm_index_table[nibble];
step_idx = CLAMP(step_idx, 0, 88);
if (i & 1) {
*out |= nibble << 4;
out++;
} else {
*out = nibble;
}
}
}
static void _compress_qoa(const Vector<float> &p_data, Vector<uint8_t> &dst_data, qoa_desc *p_desc) {
uint32_t frames_len = (p_desc->samples + QOA_FRAME_LEN - 1) / QOA_FRAME_LEN * (QOA_LMS_LEN * 4 * p_desc->channels + 8);
uint32_t slices_len = (p_desc->samples + QOA_SLICE_LEN - 1) / QOA_SLICE_LEN * 8 * p_desc->channels;
dst_data.resize(8 + frames_len + slices_len);
for (uint32_t c = 0; c < p_desc->channels; c++) {
memset(p_desc->lms[c].history, 0, sizeof(p_desc->lms[c].history));
memset(p_desc->lms[c].weights, 0, sizeof(p_desc->lms[c].weights));
p_desc->lms[c].weights[2] = -(1 << 13);
p_desc->lms[c].weights[3] = (1 << 14);
}
LocalVector<int16_t> data16;
data16.resize(QOA_FRAME_LEN * p_desc->channels);
uint8_t *dst_ptr = dst_data.ptrw();
dst_ptr += qoa_encode_header(p_desc, dst_data.ptrw());
uint32_t frame_len = QOA_FRAME_LEN;
for (uint32_t s = 0; s < p_desc->samples; s += frame_len) {
frame_len = MIN(frame_len, p_desc->samples - s);
for (uint32_t i = 0; i < frame_len * p_desc->channels; i++) {
data16[i] = CLAMP(p_data[s * p_desc->channels + i] * 32767.0, -32768, 32767);
}
dst_ptr += qoa_encode_frame(data16.ptr(), p_desc, frame_len, dst_ptr);
}
}
AudioStreamWAV();
~AudioStreamWAV();
};

View file

@ -30,7 +30,6 @@
#include "bit_map.h"
#include "core/io/image_loader.h"
#include "core/variant/typed_array.h"
void BitMap::create(const Size2i &p_size) {
@ -559,6 +558,7 @@ void BitMap::grow_mask(int p_pixels, const Rect2i &p_rect) {
bool bit_value = p_pixels > 0;
p_pixels = Math::abs(p_pixels);
const int pixels2 = p_pixels * p_pixels;
Rect2i r = Rect2i(0, 0, width, height).intersection(p_rect);
@ -588,8 +588,8 @@ void BitMap::grow_mask(int p_pixels, const Rect2i &p_rect) {
}
}
float d = Point2(j, i).distance_to(Point2(x, y)) - CMP_EPSILON;
if (d > p_pixels) {
float d = Point2(j, i).distance_squared_to(Point2(x, y)) - CMP_EPSILON2;
if (d > pixels2) {
continue;
}

View file

@ -33,7 +33,6 @@
#include "core/io/image.h"
#include "core/io/resource.h"
#include "core/io/resource_loader.h"
template <typename T>
class TypedArray;

View file

@ -65,11 +65,11 @@ Ref<SkeletonProfile> BoneMap::get_profile() const {
void BoneMap::set_profile(const Ref<SkeletonProfile> &p_profile) {
bool is_changed = profile != p_profile;
if (is_changed) {
if (!profile.is_null() && profile->is_connected("profile_updated", callable_mp(this, &BoneMap::_update_profile))) {
if (profile.is_valid() && profile->is_connected("profile_updated", callable_mp(this, &BoneMap::_update_profile))) {
profile->disconnect("profile_updated", callable_mp(this, &BoneMap::_update_profile));
}
profile = p_profile;
if (!profile.is_null()) {
if (profile.is_valid()) {
profile->connect("profile_updated", callable_mp(this, &BoneMap::_update_profile));
}
_update_profile();

View file

@ -487,7 +487,7 @@ void CameraAttributesPhysical::_bind_methods() {
ADD_GROUP("Auto Exposure", "auto_exposure_");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "auto_exposure_min_exposure_value", PROPERTY_HINT_RANGE, "-16.0,16.0,0.01,or_greater,suffix:EV100"), "set_auto_exposure_min_exposure_value", "get_auto_exposure_min_exposure_value");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "auto_exposure_max_exposure_value", PROPERTY_HINT_RANGE, "-16.0,16.0,0.01,or_greater,suffix:EV100"), "set_auto_exposure_max_exposure_value", "get_auto_exposure_max_exposure_value");
};
}
CameraAttributesPhysical::CameraAttributesPhysical() {
_update_exposure();

View file

@ -53,7 +53,7 @@ protected:
float auto_exposure_max = 64.0;
float auto_exposure_speed = 0.5;
float auto_exposure_scale = 0.4;
virtual void _update_auto_exposure(){};
virtual void _update_auto_exposure() {}
public:
virtual RID get_rid() const override;

View file

@ -47,6 +47,11 @@ void CameraTexture::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "camera_is_active"), "set_camera_active", "get_camera_active");
}
void CameraTexture::_on_format_changed() {
// FIXME: `emit_changed` is more appropriate, but causes errors for some reason.
callable_mp((Resource *)this, &Resource::emit_changed).call_deferred();
}
int CameraTexture::get_width() const {
Ref<CameraFeed> feed = CameraServer::get_singleton()->get_feed_by_id(camera_feed_id);
if (feed.is_valid()) {
@ -82,13 +87,26 @@ RID CameraTexture::get_rid() const {
}
Ref<Image> CameraTexture::get_image() const {
// not (yet) supported
return Ref<Image>();
return RenderingServer::get_singleton()->texture_2d_get(get_rid());
}
void CameraTexture::set_camera_feed_id(int p_new_id) {
Ref<CameraFeed> feed = CameraServer::get_singleton()->get_feed_by_id(camera_feed_id);
if (feed.is_valid()) {
if (feed->is_connected("format_changed", callable_mp(this, &CameraTexture::_on_format_changed))) {
feed->disconnect("format_changed", callable_mp(this, &CameraTexture::_on_format_changed));
}
}
camera_feed_id = p_new_id;
feed = CameraServer::get_singleton()->get_feed_by_id(camera_feed_id);
if (feed.is_valid()) {
feed->connect("format_changed", callable_mp(this, &CameraTexture::_on_format_changed));
}
notify_property_list_changed();
callable_mp((Resource *)this, &Resource::emit_changed).call_deferred();
}
int CameraTexture::get_camera_feed_id() const {
@ -98,6 +116,7 @@ int CameraTexture::get_camera_feed_id() const {
void CameraTexture::set_which_feed(CameraServer::FeedImage p_which) {
which_feed = p_which;
notify_property_list_changed();
callable_mp((Resource *)this, &Resource::emit_changed).call_deferred();
}
CameraServer::FeedImage CameraTexture::get_which_feed() const {
@ -109,6 +128,7 @@ void CameraTexture::set_camera_active(bool p_active) {
if (feed.is_valid()) {
feed->set_active(p_active);
notify_property_list_changed();
callable_mp((Resource *)this, &Resource::emit_changed).call_deferred();
}
}

View file

@ -43,6 +43,7 @@ private:
protected:
static void _bind_methods();
void _on_format_changed();
public:
virtual int get_width() const override;

View file

@ -157,9 +157,13 @@ void CanvasItemMaterial::flush_changes() {
}
void CanvasItemMaterial::_queue_shader_change() {
if (!_is_initialized()) {
return;
}
MutexLock lock(material_mutex);
if (_is_initialized() && !element.in_list()) {
if (!element.in_list()) {
dirty_materials.add(&element);
}
}
@ -274,6 +278,8 @@ void CanvasItemMaterial::_bind_methods() {
CanvasItemMaterial::CanvasItemMaterial() :
element(this) {
_set_material(RS::get_singleton()->material_create());
set_particles_anim_h_frames(1);
set_particles_anim_v_frames(1);
set_particles_anim_loop(false);

View file

@ -0,0 +1,46 @@
/**************************************************************************/
/* color_palette.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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. */
/**************************************************************************/
#include "color_palette.h"
void ColorPalette::set_colors(const PackedColorArray &p_colors) {
colors = p_colors;
}
PackedColorArray ColorPalette::get_colors() const {
return colors;
}
void ColorPalette::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_colors", "colors"), &ColorPalette::set_colors);
ClassDB::bind_method(D_METHOD("get_colors"), &ColorPalette::get_colors);
ADD_PROPERTY(PropertyInfo(Variant::PACKED_COLOR_ARRAY, "colors"), "set_colors", "get_colors");
}

View file

@ -0,0 +1,50 @@
/**************************************************************************/
/* color_palette.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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. */
/**************************************************************************/
#ifndef COLOR_PALETTE_H
#define COLOR_PALETTE_H
#include "core/io/resource.h"
class ColorPalette : public Resource {
GDCLASS(ColorPalette, Resource)
private:
PackedColorArray colors;
protected:
static void _bind_methods();
public:
void set_colors(const PackedColorArray &p_colors);
PackedColorArray get_colors() const;
};
#endif // COLOR_PALETTE_H

View file

@ -85,6 +85,10 @@ void CompositorEffect::_validate_property(PropertyInfo &p_property) const {
}
}
void CompositorEffect::_call_render_callback(int p_effect_callback_type, const RenderData *p_render_data) {
GDVIRTUAL_CALL(_render_callback, p_effect_callback_type, p_render_data);
}
void CompositorEffect::set_enabled(bool p_enabled) {
enabled = p_enabled;
if (rid.is_valid()) {
@ -105,7 +109,7 @@ void CompositorEffect::set_effect_callback_type(EffectCallbackType p_callback_ty
if (rid.is_valid()) {
RenderingServer *rs = RenderingServer::get_singleton();
ERR_FAIL_NULL(rs);
rs->compositor_effect_set_callback(rid, RenderingServer::CompositorEffectCallbackType(effect_callback_type), Callable(this, "_render_callback"));
rs->compositor_effect_set_callback(rid, RenderingServer::CompositorEffectCallbackType(effect_callback_type), callable_mp(this, &CompositorEffect::_call_render_callback));
}
}

View file

@ -65,6 +65,8 @@ protected:
static void _bind_methods();
void _validate_property(PropertyInfo &p_property) const;
void _call_render_callback(int p_effect_callback_type, const RenderData *p_render_data);
GDVIRTUAL2(_render_callback, int, const RenderData *)
public:

View file

@ -245,7 +245,7 @@ Ref<Image> CompressedTexture2D::get_image() const {
}
bool CompressedTexture2D::is_pixel_opaque(int p_x, int p_y) const {
if (!alpha_cache.is_valid()) {
if (alpha_cache.is_null()) {
Ref<Image> img = get_image();
if (img.is_valid()) {
if (img->is_compressed()) { //must decompress, if compressed

View file

@ -31,6 +31,7 @@
#ifndef COMPRESSED_TEXTURE_H
#define COMPRESSED_TEXTURE_H
#include "core/io/resource_loader.h"
#include "scene/resources/texture.h"
class BitMap;

File diff suppressed because it is too large Load diff

View file

@ -38,10 +38,8 @@ class Curve : public Resource {
GDCLASS(Curve, Resource);
public:
static const int MIN_X = 0.f;
static const int MAX_X = 1.f;
static const char *SIGNAL_RANGE_CHANGED;
static const char *SIGNAL_DOMAIN_CHANGED;
enum TangentMode {
TANGENT_FREE = 0,
@ -101,11 +99,18 @@ public:
real_t get_min_value() const { return _min_value; }
void set_min_value(real_t p_min);
real_t get_max_value() const { return _max_value; }
void set_max_value(real_t p_max);
real_t get_value_range() const { return _max_value - _min_value; }
real_t get_range() const { return _max_value - _min_value; }
real_t get_min_domain() const { return _min_domain; }
void set_min_domain(real_t p_min);
real_t get_max_domain() const { return _max_domain; }
void set_max_domain(real_t p_max);
real_t get_domain_range() const { return _max_domain - _min_domain; }
Array get_limits() const;
void set_limits(const Array &p_input);
real_t sample(real_t p_offset) const;
real_t sample_local_nocheck(int p_index, real_t p_local_offset) const;
@ -128,6 +133,7 @@ public:
void set_data(Array input);
void bake();
void _bake() const;
int get_bake_resolution() const { return _bake_resolution; }
void set_bake_resolution(int p_resolution);
real_t sample_baked(real_t p_offset) const;
@ -150,13 +156,14 @@ private:
TangentMode right_mode = TANGENT_FREE);
void _remove_point(int p_index);
Vector<Point> _points;
bool _baked_cache_dirty = false;
Vector<real_t> _baked_cache;
LocalVector<Point> _points;
mutable bool _baked_cache_dirty = false;
mutable Vector<real_t> _baked_cache;
int _bake_resolution = 100;
real_t _min_value = 0.0;
real_t _max_value = 1.0;
int _minmax_set_once = 0b00; // Encodes whether min and max have been set a first time, first bit for min and second for max.
real_t _min_domain = 0.0;
real_t _max_domain = 1.0;
};
VARIANT_ENUM_CAST(Curve::TangentMode)
@ -170,7 +177,7 @@ class Curve2D : public Resource {
Vector2 position;
};
Vector<Point> points;
LocalVector<Point> points;
struct BakedPoint {
real_t ofs = 0.0;
@ -258,12 +265,14 @@ class Curve3D : public Resource {
real_t tilt = 0.0;
};
Vector<Point> points;
LocalVector<Point> points;
#ifdef TOOLS_ENABLED
// For Path3DGizmo.
mutable Vector<size_t> points_in_cache;
#endif
bool closed = false;
mutable bool baked_cache_dirty = false;
mutable PackedVector3Array baked_point_cache;
mutable Vector<real_t> baked_tilt_cache;
@ -330,6 +339,8 @@ public:
Vector3 sample(int p_index, real_t p_offset) const;
Vector3 samplef(real_t p_findex) const;
void set_closed(bool p_closed);
bool is_closed() const;
void set_bake_interval(real_t p_tolerance);
real_t get_bake_interval() const;
void set_up_vector_enabled(bool p_enable);

View file

@ -130,10 +130,7 @@ int Environment::get_canvas_max_layer() const {
void Environment::set_camera_feed_id(int p_id) {
bg_camera_feed_id = p_id;
// FIXME: Disabled during Vulkan refactoring, should be ported.
#if 0
RS::get_singleton()->environment_set_camera_feed_id(environment, camera_feed_id);
#endif
RS::get_singleton()->environment_set_camera_feed_id(environment, bg_camera_feed_id);
}
int Environment::get_camera_feed_id() const {
@ -1123,7 +1120,8 @@ void Environment::_validate_property(PropertyInfo &p_property) const {
}
}
if (p_property.name == "tonemap_white" && tone_mapper == TONE_MAPPER_LINEAR) {
if (p_property.name == "tonemap_white" && (tone_mapper == TONE_MAPPER_LINEAR || tone_mapper == TONE_MAPPER_AGX)) {
// Whitepoint adjustment is not available with AgX or linear as it's hardcoded there.
p_property.usage = PROPERTY_USAGE_NO_EDITOR;
}
@ -1278,7 +1276,7 @@ void Environment::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_tonemap_white"), &Environment::get_tonemap_white);
ADD_GROUP("Tonemap", "tonemap_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "tonemap_mode", PROPERTY_HINT_ENUM, "Linear,Reinhard,Filmic,ACES"), "set_tonemapper", "get_tonemapper");
ADD_PROPERTY(PropertyInfo(Variant::INT, "tonemap_mode", PROPERTY_HINT_ENUM, "Linear,Reinhard,Filmic,ACES,AgX"), "set_tonemapper", "get_tonemapper");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "tonemap_exposure", PROPERTY_HINT_RANGE, "0,16,0.01"), "set_tonemap_exposure", "get_tonemap_exposure");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "tonemap_white", PROPERTY_HINT_RANGE, "0,16,0.01"), "set_tonemap_white", "get_tonemap_white");
@ -1583,6 +1581,7 @@ void Environment::_bind_methods() {
BIND_ENUM_CONSTANT(TONE_MAPPER_REINHARDT);
BIND_ENUM_CONSTANT(TONE_MAPPER_FILMIC);
BIND_ENUM_CONSTANT(TONE_MAPPER_ACES);
BIND_ENUM_CONSTANT(TONE_MAPPER_AGX);
BIND_ENUM_CONSTANT(GLOW_BLEND_MODE_ADDITIVE);
BIND_ENUM_CONSTANT(GLOW_BLEND_MODE_SCREEN);

View file

@ -67,6 +67,7 @@ public:
TONE_MAPPER_REINHARDT,
TONE_MAPPER_FILMIC,
TONE_MAPPER_ACES,
TONE_MAPPER_AGX,
};
enum SDFGIYScale {

View file

@ -0,0 +1,110 @@
/**************************************************************************/
/* external_texture.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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. */
/**************************************************************************/
#include "external_texture.h"
void ExternalTexture::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_size", "size"), &ExternalTexture::set_size);
ClassDB::bind_method(D_METHOD("get_external_texture_id"), &ExternalTexture::get_external_texture_id);
ClassDB::bind_method(D_METHOD("set_external_buffer_id", "external_buffer_id"), &ExternalTexture::set_external_buffer_id);
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "size"), "set_size", "get_size");
}
uint64_t ExternalTexture::get_external_texture_id() const {
_ensure_created();
return RenderingServer::get_singleton()->texture_get_native_handle(texture);
}
void ExternalTexture::set_size(const Size2 &p_size) {
if (p_size.width > 0 && p_size.height > 0 && p_size != size) {
size = p_size;
_ensure_created();
RenderingServer::get_singleton()->texture_external_update(texture, size.width, size.height, external_buffer);
emit_changed();
}
}
Size2 ExternalTexture::get_size() const {
return size;
}
void ExternalTexture::set_external_buffer_id(uint64_t p_external_buffer) {
if (p_external_buffer != external_buffer) {
external_buffer = p_external_buffer;
_ensure_created();
RenderingServer::get_singleton()->texture_external_update(texture, size.width, size.height, external_buffer);
}
}
int ExternalTexture::get_width() const {
return size.width;
}
int ExternalTexture::get_height() const {
return size.height;
}
bool ExternalTexture::has_alpha() const {
return false;
}
RID ExternalTexture::get_rid() const {
if (!texture.is_valid()) {
texture = RenderingServer::get_singleton()->texture_2d_placeholder_create();
using_placeholder = true;
}
return texture;
}
void ExternalTexture::_ensure_created() const {
if (texture.is_valid() && !using_placeholder) {
return;
}
RID new_texture = RenderingServer::get_singleton()->texture_external_create(size.width, size.height);
if (using_placeholder) {
DEV_ASSERT(texture.is_valid());
RenderingServer::get_singleton()->texture_replace(texture, new_texture);
using_placeholder = false;
} else {
texture = new_texture;
}
}
ExternalTexture::ExternalTexture() {
}
ExternalTexture::~ExternalTexture() {
if (texture.is_valid()) {
ERR_FAIL_NULL(RenderingServer::get_singleton());
RenderingServer::get_singleton()->free(texture);
}
}

View file

@ -0,0 +1,69 @@
/**************************************************************************/
/* external_texture.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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. */
/**************************************************************************/
#ifndef EXTERNAL_TEXTURE_H
#define EXTERNAL_TEXTURE_H
#include "scene/resources/texture.h"
// External textures as defined by OES_EGL_image_external (GLES) or VK_ANDROID_external_memory_android_hardware_buffer (Vulkan).
class ExternalTexture : public Texture2D {
GDCLASS(ExternalTexture, Texture2D);
private:
mutable RID texture;
mutable bool using_placeholder = false;
Size2 size = Size2(256, 256);
uint64_t external_buffer = 0;
void _ensure_created() const;
protected:
static void _bind_methods();
public:
uint64_t get_external_texture_id() const;
virtual Size2 get_size() const override;
void set_size(const Size2 &p_size);
void set_external_buffer_id(uint64_t p_external_buffer);
virtual int get_width() const override;
virtual int get_height() const override;
virtual RID get_rid() const override;
virtual bool has_alpha() const override;
ExternalTexture();
~ExternalTexture();
};
#endif // EXTERNAL_TEXTURE_H

View file

@ -32,8 +32,6 @@
#include "font.compat.inc"
#include "core/io/image_loader.h"
#include "core/io/resource_loader.h"
#include "core/string/translation.h"
#include "core/templates/hash_map.h"
#include "core/templates/hashfuncs.h"
#include "scene/resources/image_texture.h"
@ -102,23 +100,24 @@ void Font::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "fallbacks", PROPERTY_HINT_ARRAY_TYPE, MAKE_RESOURCE_TYPE_HINT("Font")), "set_fallbacks", "get_fallbacks");
}
void Font::_update_rids_fb(const Ref<Font> &p_f, int p_depth) const {
void Font::_update_rids_fb(const Font *p_f, int p_depth) const {
ERR_FAIL_COND(p_depth > MAX_FALLBACK_DEPTH);
if (p_f.is_valid()) {
if (p_f != nullptr) {
RID rid = p_f->_get_rid();
if (rid.is_valid()) {
rids.push_back(rid);
}
const TypedArray<Font> &_fallbacks = p_f->get_fallbacks();
for (int i = 0; i < _fallbacks.size(); i++) {
_update_rids_fb(_fallbacks[i], p_depth + 1);
Ref<Font> fb_font = _fallbacks[i];
_update_rids_fb(fb_font.ptr(), p_depth + 1);
}
}
}
void Font::_update_rids() const {
rids.clear();
_update_rids_fb(const_cast<Font *>(this), 0);
_update_rids_fb(this, 0);
dirty_rids = false;
}
@ -211,9 +210,10 @@ real_t Font::get_height(int p_font_size) const {
if (dirty_rids) {
_update_rids();
}
real_t ret = 0.f;
for (int i = 0; i < rids.size(); i++) {
ret = MAX(ret, TS->font_get_ascent(rids[i], p_font_size) + TS->font_get_descent(rids[i], p_font_size));
ret = MAX(ret, TS->font_get_ascent(rids.get(i), p_font_size) + TS->font_get_descent(rids.get(i), p_font_size));
}
return ret + get_spacing(TextServer::SPACING_BOTTOM) + get_spacing(TextServer::SPACING_TOP);
}
@ -224,7 +224,7 @@ real_t Font::get_ascent(int p_font_size) const {
}
real_t ret = 0.f;
for (int i = 0; i < rids.size(); i++) {
ret = MAX(ret, TS->font_get_ascent(rids[i], p_font_size));
ret = MAX(ret, TS->font_get_ascent(rids.get(i), p_font_size));
}
return ret + get_spacing(TextServer::SPACING_TOP);
}
@ -235,7 +235,7 @@ real_t Font::get_descent(int p_font_size) const {
}
real_t ret = 0.f;
for (int i = 0; i < rids.size(); i++) {
ret = MAX(ret, TS->font_get_descent(rids[i], p_font_size));
ret = MAX(ret, TS->font_get_descent(rids.get(i), p_font_size));
}
return ret + get_spacing(TextServer::SPACING_BOTTOM);
}
@ -246,7 +246,7 @@ real_t Font::get_underline_position(int p_font_size) const {
}
real_t ret = 0.f;
for (int i = 0; i < rids.size(); i++) {
ret = MAX(ret, TS->font_get_underline_position(rids[i], p_font_size));
ret = MAX(ret, TS->font_get_underline_position(rids.get(i), p_font_size));
}
return ret + get_spacing(TextServer::SPACING_TOP);
}
@ -257,7 +257,7 @@ real_t Font::get_underline_thickness(int p_font_size) const {
}
real_t ret = 0.f;
for (int i = 0; i < rids.size(); i++) {
ret = MAX(ret, TS->font_get_underline_thickness(rids[i], p_font_size));
ret = MAX(ret, TS->font_get_underline_thickness(rids.get(i), p_font_size));
}
return ret;
}
@ -477,9 +477,9 @@ Size2 Font::get_char_size(char32_t p_char, int p_font_size) const {
_update_rids();
}
for (int i = 0; i < rids.size(); i++) {
if (TS->font_has_char(rids[i], p_char)) {
int32_t glyph = TS->font_get_glyph_index(rids[i], p_font_size, p_char, 0);
return Size2(TS->font_get_glyph_advance(rids[i], p_font_size, glyph).x, get_height(p_font_size));
if (TS->font_has_char(rids.get(i), p_char)) {
int32_t glyph = TS->font_get_glyph_index(rids.get(i), p_font_size, p_char, 0);
return Size2(TS->font_get_glyph_advance(rids.get(i), p_font_size, glyph).x, get_height(p_font_size));
}
}
return Size2();
@ -490,10 +490,10 @@ real_t Font::draw_char(RID p_canvas_item, const Point2 &p_pos, char32_t p_char,
_update_rids();
}
for (int i = 0; i < rids.size(); i++) {
if (TS->font_has_char(rids[i], p_char)) {
int32_t glyph = TS->font_get_glyph_index(rids[i], p_font_size, p_char, 0);
TS->font_draw_glyph(rids[i], p_canvas_item, p_font_size, p_pos, glyph, p_modulate);
return TS->font_get_glyph_advance(rids[i], p_font_size, glyph).x;
if (TS->font_has_char(rids.get(i), p_char)) {
int32_t glyph = TS->font_get_glyph_index(rids.get(i), p_font_size, p_char, 0);
TS->font_draw_glyph(rids.get(i), p_canvas_item, p_font_size, p_pos, glyph, p_modulate);
return TS->font_get_glyph_advance(rids.get(i), p_font_size, glyph).x;
}
}
return 0.f;
@ -504,10 +504,10 @@ real_t Font::draw_char_outline(RID p_canvas_item, const Point2 &p_pos, char32_t
_update_rids();
}
for (int i = 0; i < rids.size(); i++) {
if (TS->font_has_char(rids[i], p_char)) {
int32_t glyph = TS->font_get_glyph_index(rids[i], p_font_size, p_char, 0);
TS->font_draw_glyph_outline(rids[i], p_canvas_item, p_font_size, p_size, p_pos, glyph, p_modulate);
return TS->font_get_glyph_advance(rids[i], p_font_size, glyph).x;
if (TS->font_has_char(rids.get(i), p_char)) {
int32_t glyph = TS->font_get_glyph_index(rids.get(i), p_font_size, p_char, 0);
TS->font_draw_glyph_outline(rids.get(i), p_canvas_item, p_font_size, p_size, p_pos, glyph, p_modulate);
return TS->font_get_glyph_advance(rids.get(i), p_font_size, glyph).x;
}
}
return 0.f;
@ -519,7 +519,7 @@ bool Font::has_char(char32_t p_char) const {
_update_rids();
}
for (int i = 0; i < rids.size(); i++) {
if (TS->font_has_char(rids[i], p_char)) {
if (TS->font_has_char(rids.get(i), p_char)) {
return true;
}
}
@ -532,7 +532,7 @@ String Font::get_supported_chars() const {
}
String chars;
for (int i = 0; i < rids.size(); i++) {
String data_chars = TS->font_get_supported_chars(rids[i]);
String data_chars = TS->font_get_supported_chars(rids.get(i));
for (int j = 0; j < data_chars.length(); j++) {
if (chars.find_char(data_chars[j]) == -1) {
chars += data_chars[j];
@ -605,6 +605,7 @@ _FORCE_INLINE_ void FontFile::_ensure_rid(int p_cache_index, int p_make_linked_f
TS->font_set_allow_system_fallback(cache[p_cache_index], allow_system_fallback);
TS->font_set_hinting(cache[p_cache_index], hinting);
TS->font_set_subpixel_positioning(cache[p_cache_index], subpixel_positioning);
TS->font_set_keep_rounding_remainders(cache[p_cache_index], keep_rounding_remainders);
TS->font_set_oversampling(cache[p_cache_index], oversampling);
}
}
@ -647,13 +648,13 @@ void FontFile::_convert_packed_8bit(Ref<Image> &p_source, int p_page, int p_sz)
wa[ofs_dst + 1] = r[ofs_src + 3];
}
}
Ref<Image> img_r = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_r));
Ref<Image> img_r = memnew(Image(w, h, false, Image::FORMAT_LA8, imgdata_r));
set_texture_image(0, Vector2i(p_sz, 0), p_page * 4 + 0, img_r);
Ref<Image> img_g = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_g));
Ref<Image> img_g = memnew(Image(w, h, false, Image::FORMAT_LA8, imgdata_g));
set_texture_image(0, Vector2i(p_sz, 0), p_page * 4 + 1, img_g);
Ref<Image> img_b = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_b));
Ref<Image> img_b = memnew(Image(w, h, false, Image::FORMAT_LA8, imgdata_b));
set_texture_image(0, Vector2i(p_sz, 0), p_page * 4 + 2, img_b);
Ref<Image> img_a = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_a));
Ref<Image> img_a = memnew(Image(w, h, false, Image::FORMAT_LA8, imgdata_a));
set_texture_image(0, Vector2i(p_sz, 0), p_page * 4 + 3, img_a);
}
@ -738,22 +739,22 @@ void FontFile::_convert_packed_4bit(Ref<Image> &p_source, int p_page, int p_sz)
}
}
}
Ref<Image> img_r = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_r));
Ref<Image> img_r = memnew(Image(w, h, false, Image::FORMAT_LA8, imgdata_r));
set_texture_image(0, Vector2i(p_sz, 0), p_page * 4 + 0, img_r);
Ref<Image> img_g = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_g));
Ref<Image> img_g = memnew(Image(w, h, false, Image::FORMAT_LA8, imgdata_g));
set_texture_image(0, Vector2i(p_sz, 0), p_page * 4 + 1, img_g);
Ref<Image> img_b = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_b));
Ref<Image> img_b = memnew(Image(w, h, false, Image::FORMAT_LA8, imgdata_b));
set_texture_image(0, Vector2i(p_sz, 0), p_page * 4 + 2, img_b);
Ref<Image> img_a = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_a));
Ref<Image> img_a = memnew(Image(w, h, false, Image::FORMAT_LA8, imgdata_a));
set_texture_image(0, Vector2i(p_sz, 0), p_page * 4 + 3, img_a);
Ref<Image> img_ro = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_ro));
Ref<Image> img_ro = memnew(Image(w, h, false, Image::FORMAT_LA8, imgdata_ro));
set_texture_image(0, Vector2i(p_sz, 1), p_page * 4 + 0, img_ro);
Ref<Image> img_go = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_go));
Ref<Image> img_go = memnew(Image(w, h, false, Image::FORMAT_LA8, imgdata_go));
set_texture_image(0, Vector2i(p_sz, 1), p_page * 4 + 1, img_go);
Ref<Image> img_bo = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_bo));
Ref<Image> img_bo = memnew(Image(w, h, false, Image::FORMAT_LA8, imgdata_bo));
set_texture_image(0, Vector2i(p_sz, 1), p_page * 4 + 2, img_bo);
Ref<Image> img_ao = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_ao));
Ref<Image> img_ao = memnew(Image(w, h, false, Image::FORMAT_LA8, imgdata_ao));
set_texture_image(0, Vector2i(p_sz, 1), p_page * 4 + 3, img_ao);
}
@ -806,10 +807,10 @@ void FontFile::_convert_rgba_4bit(Ref<Image> &p_source, int p_page, int p_sz) {
}
}
}
Ref<Image> img_g = memnew(Image(w, h, 0, Image::FORMAT_RGBA8, imgdata_g));
Ref<Image> img_g = memnew(Image(w, h, false, Image::FORMAT_RGBA8, imgdata_g));
set_texture_image(0, Vector2i(p_sz, 0), p_page, img_g);
Ref<Image> img_o = memnew(Image(w, h, 0, Image::FORMAT_RGBA8, imgdata_o));
Ref<Image> img_o = memnew(Image(w, h, false, Image::FORMAT_RGBA8, imgdata_o));
set_texture_image(0, Vector2i(p_sz, 1), p_page, img_o);
}
@ -838,7 +839,7 @@ void FontFile::_convert_mono_8bit(Ref<Image> &p_source, int p_page, int p_ch, in
wg[ofs_dst + 1] = r[ofs_src + p_ch];
}
}
Ref<Image> img_g = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_g));
Ref<Image> img_g = memnew(Image(w, h, false, Image::FORMAT_LA8, imgdata_g));
set_texture_image(0, Vector2i(p_sz, p_ol), p_page, img_g);
}
@ -878,10 +879,10 @@ void FontFile::_convert_mono_4bit(Ref<Image> &p_source, int p_page, int p_ch, in
}
}
}
Ref<Image> img_g = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_g));
Ref<Image> img_g = memnew(Image(w, h, false, Image::FORMAT_LA8, imgdata_g));
set_texture_image(0, Vector2i(p_sz, 0), p_page, img_g);
Ref<Image> img_o = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_o));
Ref<Image> img_o = memnew(Image(w, h, false, Image::FORMAT_LA8, imgdata_o));
set_texture_image(0, Vector2i(p_sz, p_ol), p_page, img_o);
}
@ -934,6 +935,9 @@ void FontFile::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_subpixel_positioning", "subpixel_positioning"), &FontFile::set_subpixel_positioning);
ClassDB::bind_method(D_METHOD("get_subpixel_positioning"), &FontFile::get_subpixel_positioning);
ClassDB::bind_method(D_METHOD("set_keep_rounding_remainders", "keep_rounding_remainders"), &FontFile::set_keep_rounding_remainders);
ClassDB::bind_method(D_METHOD("get_keep_rounding_remainders"), &FontFile::get_keep_rounding_remainders);
ClassDB::bind_method(D_METHOD("set_oversampling", "oversampling"), &FontFile::set_oversampling);
ClassDB::bind_method(D_METHOD("get_oversampling"), &FontFile::get_oversampling);
@ -1044,6 +1048,7 @@ void FontFile::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "font_stretch", PROPERTY_HINT_RANGE, "50,200,25", PROPERTY_USAGE_STORAGE), "set_font_stretch", "get_font_stretch");
ADD_PROPERTY(PropertyInfo(Variant::INT, "subpixel_positioning", PROPERTY_HINT_ENUM, "Disabled,Auto,One Half of a Pixel,One Quarter of a Pixel", PROPERTY_USAGE_STORAGE), "set_subpixel_positioning", "get_subpixel_positioning");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "keep_rounding_remainders", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_keep_rounding_remainders", "get_keep_rounding_remainders");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "multichannel_signed_distance_field", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_multichannel_signed_distance_field", "is_multichannel_signed_distance_field");
ADD_PROPERTY(PropertyInfo(Variant::INT, "msdf_pixel_range", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_msdf_pixel_range", "get_msdf_pixel_range");
ADD_PROPERTY(PropertyInfo(Variant::INT, "msdf_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_msdf_size", "get_msdf_size");
@ -1094,7 +1099,7 @@ bool FontFile::_set(const StringName &p_name, const Variant &p_value) {
Array textures = p_value;
for (int i = 0; i < textures.size(); i++) {
Ref<ImageTexture> tex = textures[i];
ERR_CONTINUE(!tex.is_valid());
ERR_CONTINUE(tex.is_null());
set_texture_image(0, Vector2i(16, 0), i, tex->get_image());
}
} else if (tokens.size() == 1 && tokens[0] == "chars") {
@ -1411,6 +1416,7 @@ void FontFile::reset_state() {
allow_system_fallback = true;
hinting = TextServer::HINTING_LIGHT;
subpixel_positioning = TextServer::SUBPIXEL_POSITIONING_DISABLED;
keep_rounding_remainders = true;
msdf_pixel_range = 14;
msdf_size = 128;
fixed_size = 0;
@ -1482,8 +1488,8 @@ Error FontFile::_load_bitmap_font(const String &p_path, List<String> *r_image_fi
switch (block_type) {
case 1: /* info */ {
ERR_FAIL_COND_V_MSG(block_size < 15, ERR_CANT_CREATE, "Invalid BMFont info block size.");
base_size = f->get_16();
if (base_size <= 0) {
base_size = ABS(static_cast<int16_t>(f->get_16()));
if (base_size == 0) {
base_size = 16;
}
uint8_t flags = f->get_8();
@ -1734,7 +1740,7 @@ Error FontFile::_load_bitmap_font(const String &p_path, List<String> *r_image_fi
while (true) {
String line = f->get_line();
int delimiter = line.find(" ");
int delimiter = line.find_char(' ');
String type = line.substr(0, delimiter);
int pos = delimiter + 1;
HashMap<String, String> keys;
@ -1744,7 +1750,7 @@ Error FontFile::_load_bitmap_font(const String &p_path, List<String> *r_image_fi
}
while (pos < line.size()) {
int eq = line.find("=", pos);
int eq = line.find_char('=', pos);
if (eq == -1) {
break;
}
@ -1752,14 +1758,14 @@ Error FontFile::_load_bitmap_font(const String &p_path, List<String> *r_image_fi
int end = -1;
String value;
if (line[eq + 1] == '"') {
end = line.find("\"", eq + 2);
end = line.find_char('"', eq + 2);
if (end == -1) {
break;
}
value = line.substr(eq + 2, end - 1 - eq - 1);
pos = end + 1;
} else {
end = line.find(" ", eq + 1);
end = line.find_char(' ', eq + 1);
if (end == -1) {
end = line.size();
}
@ -1776,7 +1782,10 @@ Error FontFile::_load_bitmap_font(const String &p_path, List<String> *r_image_fi
if (type == "info") {
if (keys.has("size")) {
base_size = keys["size"].to_int();
base_size = ABS(keys["size"].to_int());
if (base_size == 0) {
base_size = 16;
}
}
if (keys.has("outline")) {
outline = keys["outline"].to_int();
@ -2081,9 +2090,8 @@ void FontFile::set_data(const PackedByteArray &p_data) {
PackedByteArray FontFile::get_data() const {
if (unlikely((size_t)data.size() != data_size)) {
PackedByteArray *data_w = const_cast<PackedByteArray *>(&data);
data_w->resize(data_size);
memcpy(data_w->ptrw(), data_ptr, data_size);
data.resize(data_size);
memcpy(data.ptrw(), data_ptr, data_size);
}
return data;
}
@ -2293,6 +2301,21 @@ TextServer::SubpixelPositioning FontFile::get_subpixel_positioning() const {
return subpixel_positioning;
}
void FontFile::set_keep_rounding_remainders(bool p_keep_rounding_remainders) {
if (keep_rounding_remainders != p_keep_rounding_remainders) {
keep_rounding_remainders = p_keep_rounding_remainders;
for (int i = 0; i < cache.size(); i++) {
_ensure_rid(i);
TS->font_set_keep_rounding_remainders(cache[i], keep_rounding_remainders);
}
emit_changed();
}
}
bool FontFile::get_keep_rounding_remainders() const {
return keep_rounding_remainders;
}
void FontFile::set_oversampling(real_t p_oversampling) {
if (oversampling != p_oversampling) {
oversampling = p_oversampling;
@ -2850,10 +2873,11 @@ void FontVariation::_update_rids() const {
const TypedArray<Font> &base_fallbacks = f->get_fallbacks();
for (int i = 0; i < base_fallbacks.size(); i++) {
_update_rids_fb(base_fallbacks[i], 0);
Ref<Font> fb_font = base_fallbacks[i];
_update_rids_fb(fb_font.ptr(), 0);
}
} else {
_update_rids_fb(const_cast<FontVariation *>(this), 0);
_update_rids_fb(this, 0);
}
dirty_rids = false;
}
@ -2900,7 +2924,7 @@ Ref<Font> FontVariation::get_base_font() const {
Ref<Font> FontVariation::_get_base_font_or_default() const {
if (theme_font.is_valid()) {
theme_font->disconnect_changed(callable_mp(reinterpret_cast<Font *>(const_cast<FontVariation *>(this)), &Font::_invalidate_rids));
theme_font->disconnect_changed(callable_mp(static_cast<Font *>(const_cast<FontVariation *>(this)), &Font::_invalidate_rids));
theme_font.unref();
}
@ -2909,13 +2933,13 @@ Ref<Font> FontVariation::_get_base_font_or_default() const {
}
StringName theme_name = "font";
List<StringName> theme_types;
ThemeDB::get_singleton()->get_native_type_dependencies(get_class_name(), &theme_types);
Vector<StringName> theme_types;
ThemeDB::get_singleton()->get_native_type_dependencies(get_class_name(), theme_types);
ThemeContext *global_context = ThemeDB::get_singleton()->get_default_theme_context();
List<Ref<Theme>> themes = global_context->get_themes();
Vector<Ref<Theme>> themes = global_context->get_themes();
if (Engine::get_singleton()->is_editor_hint()) {
themes.push_front(ThemeDB::get_singleton()->get_project_theme());
themes.insert(0, ThemeDB::get_singleton()->get_project_theme());
}
for (const Ref<Theme> &theme : themes) {
@ -2934,7 +2958,7 @@ Ref<Font> FontVariation::_get_base_font_or_default() const {
}
if (f.is_valid()) {
theme_font = f;
theme_font->connect_changed(callable_mp(reinterpret_cast<Font *>(const_cast<FontVariation *>(this)), &Font::_invalidate_rids), CONNECT_REFERENCE_COUNTED);
theme_font->connect_changed(callable_mp(static_cast<Font *>(const_cast<FontVariation *>(this)), &Font::_invalidate_rids), CONNECT_REFERENCE_COUNTED);
}
return f;
}
@ -2944,7 +2968,7 @@ Ref<Font> FontVariation::_get_base_font_or_default() const {
if (!_is_base_cyclic(f, 0)) {
if (f.is_valid()) {
theme_font = f;
theme_font->connect_changed(callable_mp(reinterpret_cast<Font *>(const_cast<FontVariation *>(this)), &Font::_invalidate_rids), CONNECT_REFERENCE_COUNTED);
theme_font->connect_changed(callable_mp(static_cast<Font *>(const_cast<FontVariation *>(this)), &Font::_invalidate_rids), CONNECT_REFERENCE_COUNTED);
}
return f;
}
@ -3082,6 +3106,9 @@ void SystemFont::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_subpixel_positioning", "subpixel_positioning"), &SystemFont::set_subpixel_positioning);
ClassDB::bind_method(D_METHOD("get_subpixel_positioning"), &SystemFont::get_subpixel_positioning);
ClassDB::bind_method(D_METHOD("set_keep_rounding_remainders", "keep_rounding_remainders"), &SystemFont::set_keep_rounding_remainders);
ClassDB::bind_method(D_METHOD("get_keep_rounding_remainders"), &SystemFont::get_keep_rounding_remainders);
ClassDB::bind_method(D_METHOD("set_multichannel_signed_distance_field", "msdf"), &SystemFont::set_multichannel_signed_distance_field);
ClassDB::bind_method(D_METHOD("is_multichannel_signed_distance_field"), &SystemFont::is_multichannel_signed_distance_field);
@ -3113,6 +3140,7 @@ void SystemFont::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "force_autohinter"), "set_force_autohinter", "is_force_autohinter");
ADD_PROPERTY(PropertyInfo(Variant::INT, "hinting", PROPERTY_HINT_ENUM, "None,Light,Normal"), "set_hinting", "get_hinting");
ADD_PROPERTY(PropertyInfo(Variant::INT, "subpixel_positioning", PROPERTY_HINT_ENUM, "Disabled,Auto,One Half of a Pixel,One Quarter of a Pixel"), "set_subpixel_positioning", "get_subpixel_positioning");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "keep_rounding_remainders"), "set_keep_rounding_remainders", "get_keep_rounding_remainders");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "multichannel_signed_distance_field"), "set_multichannel_signed_distance_field", "is_multichannel_signed_distance_field");
ADD_PROPERTY(PropertyInfo(Variant::INT, "msdf_pixel_range"), "set_msdf_pixel_range", "get_msdf_pixel_range");
ADD_PROPERTY(PropertyInfo(Variant::INT, "msdf_size"), "set_msdf_size", "get_msdf_size");
@ -3131,10 +3159,11 @@ void SystemFont::_update_rids() const {
const TypedArray<Font> &base_fallbacks = f->get_fallbacks();
for (int i = 0; i < base_fallbacks.size(); i++) {
_update_rids_fb(base_fallbacks[i], 0);
Ref<Font> fb_font = base_fallbacks[i];
_update_rids_fb(fb_font.ptr(), 0);
}
} else {
_update_rids_fb(const_cast<SystemFont *>(this), 0);
_update_rids_fb(this, 0);
}
dirty_rids = false;
}
@ -3145,7 +3174,7 @@ void SystemFont::_update_base_font() {
base_font.unref();
}
face_indeces.clear();
face_indices.clear();
ftr_weight = 0;
ftr_stretch = 0;
ftr_italic = 0;
@ -3183,17 +3212,17 @@ void SystemFont::_update_base_font() {
score += 30;
}
if (score > best_score) {
face_indeces.clear();
face_indices.clear();
}
if (score >= best_score) {
best_score = score;
face_indeces.push_back(i);
face_indices.push_back(i);
}
}
if (face_indeces.is_empty()) {
face_indeces.push_back(0);
if (face_indices.is_empty()) {
face_indices.push_back(0);
}
file->set_face_index(0, face_indeces[0]);
file->set_face_index(0, face_indices[0]);
// If it's a variable font, apply weight, stretch and italic coordinates to match requested style.
if (best_score != 150) {
@ -3217,6 +3246,7 @@ void SystemFont::_update_base_font() {
file->set_allow_system_fallback(allow_system_fallback);
file->set_hinting(hinting);
file->set_subpixel_positioning(subpixel_positioning);
file->set_keep_rounding_remainders(keep_rounding_remainders);
file->set_multichannel_signed_distance_field(msdf);
file->set_msdf_pixel_range(msdf_pixel_range);
file->set_msdf_size(msdf_size);
@ -3246,7 +3276,7 @@ void SystemFont::reset_state() {
}
names.clear();
face_indeces.clear();
face_indices.clear();
ftr_weight = 0;
ftr_stretch = 0;
ftr_italic = 0;
@ -3260,6 +3290,7 @@ void SystemFont::reset_state() {
allow_system_fallback = true;
hinting = TextServer::HINTING_LIGHT;
subpixel_positioning = TextServer::SUBPIXEL_POSITIONING_DISABLED;
keep_rounding_remainders = true;
oversampling = 0.f;
msdf = false;
@ -3268,7 +3299,7 @@ void SystemFont::reset_state() {
Ref<Font> SystemFont::_get_base_font_or_default() const {
if (theme_font.is_valid()) {
theme_font->disconnect_changed(callable_mp(reinterpret_cast<Font *>(const_cast<SystemFont *>(this)), &Font::_invalidate_rids));
theme_font->disconnect_changed(callable_mp(static_cast<Font *>(const_cast<SystemFont *>(this)), &Font::_invalidate_rids));
theme_font.unref();
}
@ -3277,8 +3308,8 @@ Ref<Font> SystemFont::_get_base_font_or_default() const {
}
StringName theme_name = "font";
List<StringName> theme_types;
ThemeDB::get_singleton()->get_native_type_dependencies(get_class_name(), &theme_types);
Vector<StringName> theme_types;
ThemeDB::get_singleton()->get_native_type_dependencies(get_class_name(), theme_types);
ThemeContext *global_context = ThemeDB::get_singleton()->get_default_theme_context();
for (const Ref<Theme> &theme : global_context->get_themes()) {
@ -3297,7 +3328,7 @@ Ref<Font> SystemFont::_get_base_font_or_default() const {
}
if (f.is_valid()) {
theme_font = f;
theme_font->connect_changed(callable_mp(reinterpret_cast<Font *>(const_cast<SystemFont *>(this)), &Font::_invalidate_rids), CONNECT_REFERENCE_COUNTED);
theme_font->connect_changed(callable_mp(static_cast<Font *>(const_cast<SystemFont *>(this)), &Font::_invalidate_rids), CONNECT_REFERENCE_COUNTED);
}
return f;
}
@ -3307,7 +3338,7 @@ Ref<Font> SystemFont::_get_base_font_or_default() const {
if (!_is_base_cyclic(f, 0)) {
if (f.is_valid()) {
theme_font = f;
theme_font->connect_changed(callable_mp(reinterpret_cast<Font *>(const_cast<SystemFont *>(this)), &Font::_invalidate_rids), CONNECT_REFERENCE_COUNTED);
theme_font->connect_changed(callable_mp(static_cast<Font *>(const_cast<SystemFont *>(this)), &Font::_invalidate_rids), CONNECT_REFERENCE_COUNTED);
}
return f;
}
@ -3413,6 +3444,20 @@ TextServer::SubpixelPositioning SystemFont::get_subpixel_positioning() const {
return subpixel_positioning;
}
void SystemFont::set_keep_rounding_remainders(bool p_keep_rounding_remainders) {
if (keep_rounding_remainders != p_keep_rounding_remainders) {
keep_rounding_remainders = p_keep_rounding_remainders;
if (base_font.is_valid()) {
base_font->set_keep_rounding_remainders(keep_rounding_remainders);
}
emit_changed();
}
}
bool SystemFont::get_keep_rounding_remainders() const {
return keep_rounding_remainders;
}
void SystemFont::set_multichannel_signed_distance_field(bool p_msdf) {
if (msdf != p_msdf) {
msdf = p_msdf;
@ -3535,9 +3580,9 @@ RID SystemFont::find_variation(const Dictionary &p_variation_coordinates, int p_
var[TS->name_to_tag("italic")] = ftr_italic;
}
if (!face_indeces.is_empty()) {
int face_index = CLAMP(p_face_index, 0, face_indeces.size() - 1);
return f->find_variation(var, face_indeces[face_index], p_strength, p_transform, p_spacing_top, p_spacing_bottom, p_spacing_space, p_spacing_glyph, p_baseline_offset);
if (!face_indices.is_empty()) {
int face_index = CLAMP(p_face_index, 0, face_indices.size() - 1);
return f->find_variation(var, face_indices[face_index], p_strength, p_transform, p_spacing_top, p_spacing_bottom, p_spacing_space, p_spacing_glyph, p_baseline_offset);
} else {
return f->find_variation(var, 0, p_strength, p_transform, p_spacing_top, p_spacing_bottom, p_spacing_space, p_spacing_glyph, p_baseline_offset);
}
@ -3548,7 +3593,7 @@ RID SystemFont::find_variation(const Dictionary &p_variation_coordinates, int p_
RID SystemFont::_get_rid() const {
Ref<Font> f = _get_base_font_or_default();
if (f.is_valid()) {
if (!face_indeces.is_empty()) {
if (!face_indices.is_empty()) {
Dictionary var;
if (ftr_weight > 0) {
var[TS->name_to_tag("weight")] = ftr_weight;
@ -3559,7 +3604,7 @@ RID SystemFont::_get_rid() const {
if (ftr_italic > 0) {
var[TS->name_to_tag("italic")] = ftr_italic;
}
return f->find_variation(var, face_indeces[0]);
return f->find_variation(var, face_indices[0]);
} else {
return f->_get_rid();
}
@ -3568,7 +3613,7 @@ RID SystemFont::_get_rid() const {
}
int64_t SystemFont::get_face_count() const {
return face_indeces.size();
return face_indices.size();
}
SystemFont::SystemFont() {

View file

@ -33,7 +33,6 @@
#include "core/io/resource.h"
#include "core/templates/lru.h"
#include "core/templates/rb_map.h"
#include "scene/resources/texture.h"
#include "servers/text_server.h"
@ -97,7 +96,7 @@ protected:
static void _bind_methods();
virtual void _update_rids_fb(const Ref<Font> &p_f, int p_depth) const;
virtual void _update_rids_fb(const Font *p_f, int p_depth) const;
virtual void _update_rids() const;
virtual void reset_state() override;
@ -183,7 +182,7 @@ class FontFile : public Font {
// Font source data.
const uint8_t *data_ptr = nullptr;
size_t data_size = 0;
PackedByteArray data;
mutable PackedByteArray data;
TextServer::FontAntialiasing antialiasing = TextServer::FONT_ANTIALIASING_GRAY;
bool mipmaps = false;
@ -197,6 +196,7 @@ class FontFile : public Font {
bool allow_system_fallback = true;
TextServer::Hinting hinting = TextServer::HINTING_LIGHT;
TextServer::SubpixelPositioning subpixel_positioning = TextServer::SUBPIXEL_POSITIONING_AUTO;
bool keep_rounding_remainders = true;
real_t oversampling = 0.f;
#ifndef DISABLE_DEPRECATED
@ -280,6 +280,9 @@ public:
virtual void set_subpixel_positioning(TextServer::SubpixelPositioning p_subpixel);
virtual TextServer::SubpixelPositioning get_subpixel_positioning() const;
virtual void set_keep_rounding_remainders(bool p_keep_rounding_remainders);
virtual bool get_keep_rounding_remainders() const;
virtual void set_oversampling(real_t p_oversampling);
virtual real_t get_oversampling() const;
@ -468,7 +471,7 @@ class SystemFont : public Font {
mutable Ref<Font> theme_font;
Ref<FontFile> base_font;
Vector<int> face_indeces;
Vector<int> face_indices;
int ftr_weight = 0;
int ftr_stretch = 0;
int ftr_italic = 0;
@ -480,6 +483,7 @@ class SystemFont : public Font {
bool allow_system_fallback = true;
TextServer::Hinting hinting = TextServer::HINTING_LIGHT;
TextServer::SubpixelPositioning subpixel_positioning = TextServer::SUBPIXEL_POSITIONING_AUTO;
bool keep_rounding_remainders = true;
real_t oversampling = 0.f;
bool msdf = false;
int msdf_pixel_range = 16;
@ -518,6 +522,9 @@ public:
virtual void set_subpixel_positioning(TextServer::SubpixelPositioning p_subpixel);
virtual TextServer::SubpixelPositioning get_subpixel_positioning() const;
virtual void set_keep_rounding_remainders(bool p_keep_rounding_remainders);
virtual bool get_keep_rounding_remainders() const;
virtual void set_oversampling(real_t p_oversampling);
virtual real_t get_oversampling() const;

View file

@ -33,10 +33,10 @@
Gradient::Gradient() {
//Set initial gradient transition from black to white
points.resize(2);
points.write[0].color = Color(0, 0, 0, 1);
points.write[0].offset = 0;
points.write[1].color = Color(1, 1, 1, 1);
points.write[1].offset = 1;
points[0].color = Color(0, 0, 0, 1);
points[0].offset = 0;
points[1].color = Color(1, 1, 1, 1);
points[1].offset = 1;
}
Gradient::~Gradient() {
@ -96,7 +96,7 @@ void Gradient::_validate_property(PropertyInfo &p_property) const {
Vector<float> Gradient::get_offsets() const {
Vector<float> offsets;
offsets.resize(points.size());
for (int i = 0; i < points.size(); i++) {
for (uint32_t i = 0; i < points.size(); i++) {
offsets.write[i] = points[i].offset;
}
return offsets;
@ -105,7 +105,7 @@ Vector<float> Gradient::get_offsets() const {
Vector<Color> Gradient::get_colors() const {
Vector<Color> colors;
colors.resize(points.size());
for (int i = 0; i < points.size(); i++) {
for (uint32_t i = 0; i < points.size(); i++) {
colors.write[i] = points[i].color;
}
return colors;
@ -140,8 +140,8 @@ Gradient::ColorSpace Gradient::get_interpolation_color_space() {
void Gradient::set_offsets(const Vector<float> &p_offsets) {
points.resize(p_offsets.size());
for (int i = 0; i < points.size(); i++) {
points.write[i].offset = p_offsets[i];
for (uint32_t i = 0; i < points.size(); i++) {
points[i].offset = p_offsets[i];
}
is_sorted = false;
emit_changed();
@ -152,16 +152,12 @@ void Gradient::set_colors(const Vector<Color> &p_colors) {
is_sorted = false;
}
points.resize(p_colors.size());
for (int i = 0; i < points.size(); i++) {
points.write[i].color = p_colors[i];
for (uint32_t i = 0; i < points.size(); i++) {
points[i].color = p_colors[i];
}
emit_changed();
}
Vector<Gradient::Point> &Gradient::get_points() {
return points;
}
void Gradient::add_point(float p_offset, const Color &p_color) {
Point p;
p.offset = p_offset;
@ -173,15 +169,15 @@ void Gradient::add_point(float p_offset, const Color &p_color) {
}
void Gradient::remove_point(int p_index) {
ERR_FAIL_INDEX(p_index, points.size());
ERR_FAIL_UNSIGNED_INDEX((uint32_t)p_index, points.size());
ERR_FAIL_COND(points.size() <= 1);
points.remove_at(p_index);
emit_changed();
}
void Gradient::reverse() {
for (int i = 0; i < points.size(); i++) {
points.write[i].offset = 1.0 - points[i].offset;
for (uint32_t i = 0; i < points.size(); i++) {
points[i].offset = 1.0 - points[i].offset;
}
is_sorted = false;
@ -189,35 +185,29 @@ void Gradient::reverse() {
emit_changed();
}
void Gradient::set_points(const Vector<Gradient::Point> &p_points) {
points = p_points;
is_sorted = false;
emit_changed();
}
void Gradient::set_offset(int pos, const float offset) {
ERR_FAIL_INDEX(pos, points.size());
ERR_FAIL_UNSIGNED_INDEX((uint32_t)pos, points.size());
_update_sorting();
points.write[pos].offset = offset;
points[pos].offset = offset;
is_sorted = false;
emit_changed();
}
float Gradient::get_offset(int pos) {
ERR_FAIL_INDEX_V(pos, points.size(), 0.0);
ERR_FAIL_UNSIGNED_INDEX_V((uint32_t)pos, points.size(), 0.0);
_update_sorting();
return points[pos].offset;
}
void Gradient::set_color(int pos, const Color &color) {
ERR_FAIL_INDEX(pos, points.size());
ERR_FAIL_UNSIGNED_INDEX((uint32_t)pos, points.size());
_update_sorting();
points.write[pos].color = color;
points[pos].color = color;
emit_changed();
}
Color Gradient::get_color(int pos) {
ERR_FAIL_INDEX_V(pos, points.size(), Color());
ERR_FAIL_UNSIGNED_INDEX_V((uint32_t)pos, points.size(), Color());
_update_sorting();
return points[pos].color;
}

View file

@ -61,7 +61,7 @@ public:
};
private:
Vector<Point> points;
LocalVector<Point> points;
bool is_sorted = true;
InterpolationMode interpolation_mode = GRADIENT_INTERPOLATE_LINEAR;
ColorSpace interpolation_color_space = GRADIENT_COLOR_SPACE_SRGB;
@ -129,8 +129,6 @@ public:
void add_point(float p_offset, const Color &p_color);
void remove_point(int p_index);
void set_points(const Vector<Point> &p_points);
Vector<Point> &get_points();
void reverse();
void set_offset(int pos, const float offset);
@ -187,7 +185,7 @@ public:
}
int first = middle;
int second = middle + 1;
if (second >= points.size()) {
if (second >= (int)points.size()) {
return points[points.size() - 1].color;
}
if (first < 0) {
@ -212,7 +210,7 @@ public:
case GRADIENT_INTERPOLATE_CUBIC: {
int p0 = first - 1;
int p3 = second + 1;
if (p3 >= points.size()) {
if (p3 >= (int)points.size()) {
p3 = second;
}
if (p0 < 0) {

View file

@ -85,7 +85,7 @@ void GradientTexture1D::_queue_update() {
callable_mp(this, &GradientTexture1D::update_now).call_deferred();
}
void GradientTexture1D::_update() {
void GradientTexture1D::_update() const {
update_pending = false;
if (gradient.is_null()) {
@ -172,14 +172,14 @@ RID GradientTexture1D::get_rid() const {
}
Ref<Image> GradientTexture1D::get_image() const {
const_cast<GradientTexture1D *>(this)->update_now();
update_now();
if (!texture.is_valid()) {
return Ref<Image>();
}
return RenderingServer::get_singleton()->texture_2d_get(texture);
}
void GradientTexture1D::update_now() {
void GradientTexture1D::update_now() const {
if (update_pending) {
_update();
}
@ -225,7 +225,7 @@ void GradientTexture2D::_queue_update() {
callable_mp(this, &GradientTexture2D::update_now).call_deferred();
}
void GradientTexture2D::_update() {
void GradientTexture2D::_update() const {
update_pending = false;
if (gradient.is_null()) {
@ -405,14 +405,14 @@ RID GradientTexture2D::get_rid() const {
}
Ref<Image> GradientTexture2D::get_image() const {
const_cast<GradientTexture2D *>(this)->update_now();
update_now();
if (!texture.is_valid()) {
return Ref<Image>();
}
return RenderingServer::get_singleton()->texture_2d_get(texture);
}
void GradientTexture2D::update_now() {
void GradientTexture2D::update_now() const {
if (update_pending) {
_update();
}

View file

@ -38,13 +38,13 @@ class GradientTexture1D : public Texture2D {
private:
Ref<Gradient> gradient;
bool update_pending = false;
mutable bool update_pending = false;
mutable RID texture;
int width = 256;
bool use_hdr = false;
void _queue_update();
void _update();
void _update() const;
protected:
static void _bind_methods();
@ -64,7 +64,7 @@ public:
virtual bool has_alpha() const override { return true; }
virtual Ref<Image> get_image() const override;
void update_now();
void update_now() const;
GradientTexture1D();
virtual ~GradientTexture1D();
@ -102,9 +102,9 @@ private:
float _get_gradient_offset_at(int x, int y) const;
bool update_pending = false;
mutable bool update_pending = false;
void _queue_update();
void _update();
void _update() const;
protected:
static void _bind_methods();
@ -134,7 +134,7 @@ public:
virtual RID get_rid() const override;
virtual bool has_alpha() const override { return true; }
virtual Ref<Image> get_image() const override;
void update_now();
void update_now() const;
GradientTexture2D();
virtual ~GradientTexture2D();

View file

@ -174,7 +174,7 @@ void ImageTexture::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, cons
}
bool ImageTexture::is_pixel_opaque(int p_x, int p_y) const {
if (!alpha_cache.is_valid()) {
if (alpha_cache.is_null()) {
Ref<Image> img = get_image();
if (img.is_valid()) {
if (img->is_compressed()) { //must decompress, if compressed

View file

@ -312,6 +312,8 @@ void ImmediateMesh::surface_end() {
uses_uv2s = false;
surface_active = false;
emit_changed();
}
void ImmediateMesh::clear_surfaces() {

View file

@ -38,6 +38,9 @@ void LabelSettings::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_line_spacing", "spacing"), &LabelSettings::set_line_spacing);
ClassDB::bind_method(D_METHOD("get_line_spacing"), &LabelSettings::get_line_spacing);
ClassDB::bind_method(D_METHOD("set_paragraph_spacing", "spacing"), &LabelSettings::set_paragraph_spacing);
ClassDB::bind_method(D_METHOD("get_paragraph_spacing"), &LabelSettings::get_paragraph_spacing);
ClassDB::bind_method(D_METHOD("set_font", "font"), &LabelSettings::set_font);
ClassDB::bind_method(D_METHOD("get_font"), &LabelSettings::get_font);
@ -63,6 +66,7 @@ void LabelSettings::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_shadow_offset"), &LabelSettings::get_shadow_offset);
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "line_spacing", PROPERTY_HINT_NONE, "suffix:px"), "set_line_spacing", "get_line_spacing");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "paragraph_spacing", PROPERTY_HINT_NONE, "suffix:px"), "set_paragraph_spacing", "get_paragraph_spacing");
ADD_GROUP("Font", "font_");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "font", PROPERTY_HINT_RESOURCE_TYPE, "Font"), "set_font", "get_font");
@ -90,6 +94,17 @@ real_t LabelSettings::get_line_spacing() const {
return line_spacing;
}
void LabelSettings::set_paragraph_spacing(real_t p_spacing) {
if (paragraph_spacing != p_spacing) {
paragraph_spacing = p_spacing;
emit_changed();
}
}
real_t LabelSettings::get_paragraph_spacing() const {
return paragraph_spacing;
}
void LabelSettings::set_font(const Ref<Font> &p_font) {
if (font != p_font) {
if (font.is_valid()) {

View file

@ -40,6 +40,7 @@ class LabelSettings : public Resource {
GDCLASS(LabelSettings, Resource);
real_t line_spacing = 3;
real_t paragraph_spacing = 0;
Ref<Font> font;
int font_size = Font::DEFAULT_FONT_SIZE;
@ -61,6 +62,9 @@ public:
void set_line_spacing(real_t p_spacing);
real_t get_line_spacing() const;
void set_paragraph_spacing(real_t p_spacing);
real_t get_paragraph_spacing() const;
void set_font(const Ref<Font> &p_font);
Ref<Font> get_font() const;

View file

@ -37,7 +37,7 @@
#include "scene/main/scene_tree.h"
void Material::set_next_pass(const Ref<Material> &p_pass) {
for (Ref<Material> pass_child = p_pass; pass_child != nullptr; pass_child = pass_child->get_next_pass()) {
for (Ref<Material> pass_child = p_pass; pass_child.is_valid(); pass_child = pass_child->get_next_pass()) {
ERR_FAIL_COND_MSG(pass_child == this, "Can't set as next_pass one of its parents to prevent crashes due to recursive loop.");
}
@ -46,11 +46,15 @@ void Material::set_next_pass(const Ref<Material> &p_pass) {
}
next_pass = p_pass;
RID next_pass_rid;
if (next_pass.is_valid()) {
next_pass_rid = next_pass->get_rid();
if (material.is_valid()) {
RID next_pass_rid;
if (next_pass.is_valid()) {
next_pass_rid = next_pass->get_rid();
}
RS::get_singleton()->material_set_next_pass(material, next_pass_rid);
}
RS::get_singleton()->material_set_next_pass(material, next_pass_rid);
}
Ref<Material> Material::get_next_pass() const {
@ -61,7 +65,10 @@ void Material::set_render_priority(int p_priority) {
ERR_FAIL_COND(p_priority < RENDER_PRIORITY_MIN);
ERR_FAIL_COND(p_priority > RENDER_PRIORITY_MAX);
render_priority = p_priority;
RS::get_singleton()->material_set_render_priority(material, p_priority);
if (material.is_valid()) {
RS::get_singleton()->material_set_render_priority(material, p_priority);
}
}
int Material::get_render_priority() const {
@ -92,9 +99,13 @@ void Material::_mark_initialized(const Callable &p_add_to_dirty_list, const Call
if (ResourceLoader::is_within_load()) {
DEV_ASSERT(init_state != INIT_STATE_READY);
if (init_state == INIT_STATE_UNINITIALIZED) { // Prevent queueing twice.
init_state = INIT_STATE_INITIALIZING;
callable_mp(this, &Material::_mark_ready).call_deferred();
p_update_shader.call_deferred();
if (p_update_shader.is_valid()) {
init_state = INIT_STATE_INITIALIZING;
callable_mp(this, &Material::_mark_ready).call_deferred();
p_update_shader.call_deferred();
} else {
init_state = INIT_STATE_READY;
}
}
} else {
// Straightforward conditions.
@ -113,12 +124,12 @@ void Material::inspect_native_shader_code() {
RID Material::get_shader_rid() const {
RID ret;
GDVIRTUAL_REQUIRED_CALL(_get_shader_rid, ret);
GDVIRTUAL_CALL(_get_shader_rid, ret);
return ret;
}
Shader::Mode Material::get_shader_mode() const {
Shader::Mode ret = Shader::MODE_MAX;
GDVIRTUAL_REQUIRED_CALL(_get_shader_mode, ret);
GDVIRTUAL_CALL(_get_shader_mode, ret);
return ret;
}
@ -165,13 +176,14 @@ void Material::_bind_methods() {
}
Material::Material() {
material = RenderingServer::get_singleton()->material_create();
render_priority = 0;
}
Material::~Material() {
ERR_FAIL_NULL(RenderingServer::get_singleton());
RenderingServer::get_singleton()->free(material);
if (material.is_valid()) {
ERR_FAIL_NULL(RenderingServer::get_singleton());
RenderingServer::get_singleton()->free(material);
}
}
///////////////////////////////////
@ -227,7 +239,7 @@ bool ShaderMaterial::_get(const StringName &p_name, Variant &r_ret) const {
}
void ShaderMaterial::_get_property_list(List<PropertyInfo> *p_list) const {
if (!shader.is_null()) {
if (shader.is_valid()) {
List<PropertyInfo> list;
shader->get_shader_uniform_list(&list, true);
@ -374,12 +386,11 @@ void ShaderMaterial::_get_property_list(List<PropertyInfo> *p_list) const {
bool ShaderMaterial::_property_can_revert(const StringName &p_name) const {
if (shader.is_valid()) {
const StringName *pr = remap_cache.getptr(p_name);
if (pr) {
Variant default_value = RenderingServer::get_singleton()->shader_get_parameter_default(shader->get_rid(), *pr);
Variant current_value = get_shader_parameter(*pr);
return default_value.get_type() != Variant::NIL && default_value != current_value;
if (remap_cache.has(p_name)) {
return true;
}
const String sname = p_name;
return sname == "render_priority" || sname == "next_pass";
}
return false;
}
@ -390,6 +401,12 @@ bool ShaderMaterial::_property_get_revert(const StringName &p_name, Variant &r_p
if (pr) {
r_property = RenderingServer::get_singleton()->shader_get_parameter_default(shader->get_rid(), *pr);
return true;
} else if (p_name == "render_priority") {
r_property = 0;
return true;
} else if (p_name == "next_pass") {
r_property = Variant();
return true;
}
}
return false;
@ -414,7 +431,11 @@ void ShaderMaterial::set_shader(const Ref<Shader> &p_shader) {
}
}
RS::get_singleton()->material_set_shader(_get_material(), rid);
RID material_rid = _get_material();
if (material_rid.is_valid()) {
RS::get_singleton()->material_set_shader(material_rid, rid);
}
notify_property_list_changed(); //properties for shader exposed
emit_changed();
}
@ -424,9 +445,12 @@ Ref<Shader> ShaderMaterial::get_shader() const {
}
void ShaderMaterial::set_shader_parameter(const StringName &p_param, const Variant &p_value) {
RID material_rid = _get_material();
if (p_value.get_type() == Variant::NIL) {
param_cache.erase(p_param);
RS::get_singleton()->material_set_param(_get_material(), p_param, Variant());
if (material_rid.is_valid()) {
RS::get_singleton()->material_set_param(material_rid, p_param, Variant());
}
} else {
Variant *v = param_cache.getptr(p_param);
if (!v) {
@ -441,12 +465,15 @@ void ShaderMaterial::set_shader_parameter(const StringName &p_param, const Varia
RID tex_rid = p_value;
if (tex_rid == RID()) {
param_cache.erase(p_param);
RS::get_singleton()->material_set_param(_get_material(), p_param, Variant());
} else {
RS::get_singleton()->material_set_param(_get_material(), p_param, tex_rid);
if (material_rid.is_valid()) {
RS::get_singleton()->material_set_param(material_rid, p_param, Variant());
}
} else if (material_rid.is_valid()) {
RS::get_singleton()->material_set_param(material_rid, p_param, tex_rid);
}
} else {
RS::get_singleton()->material_set_param(_get_material(), p_param, p_value);
} else if (material_rid.is_valid()) {
RS::get_singleton()->material_set_param(material_rid, p_param, p_value);
}
}
}
@ -463,6 +490,32 @@ void ShaderMaterial::_shader_changed() {
notify_property_list_changed(); //update all properties
}
void ShaderMaterial::_check_material_rid() const {
MutexLock lock(material_rid_mutex);
if (_get_material().is_null()) {
RID shader_rid = shader.is_valid() ? shader->get_rid() : RID();
RID next_pass_rid;
if (get_next_pass().is_valid()) {
next_pass_rid = get_next_pass()->get_rid();
}
_set_material(RS::get_singleton()->material_create_from_shader(next_pass_rid, get_render_priority(), shader_rid));
for (KeyValue<StringName, Variant> param : param_cache) {
if (param.value.get_type() == Variant::OBJECT) {
RID tex_rid = param.value;
if (tex_rid.is_valid()) {
RS::get_singleton()->material_set_param(_get_material(), param.key, tex_rid);
} else {
RS::get_singleton()->material_set_param(_get_material(), param.key, Variant());
}
} else {
RS::get_singleton()->material_set_param(_get_material(), param.key, param.value);
}
}
}
}
void ShaderMaterial::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_shader", "shader"), &ShaderMaterial::set_shader);
ClassDB::bind_method(D_METHOD("get_shader"), &ShaderMaterial::get_shader);
@ -503,6 +556,12 @@ Shader::Mode ShaderMaterial::get_shader_mode() const {
return Shader::MODE_SPATIAL;
}
}
RID ShaderMaterial::get_rid() const {
_check_material_rid();
return Material::get_rid();
}
RID ShaderMaterial::get_shader_rid() const {
if (shader.is_valid()) {
return shader->get_rid();
@ -512,6 +571,7 @@ RID ShaderMaterial::get_shader_rid() const {
}
ShaderMaterial::ShaderMaterial() {
// Material RID will be empty until it is required.
}
ShaderMaterial::~ShaderMaterial() {
@ -519,10 +579,11 @@ ShaderMaterial::~ShaderMaterial() {
/////////////////////////////////
HashMap<BaseMaterial3D::MaterialKey, BaseMaterial3D::ShaderData, BaseMaterial3D::MaterialKey> BaseMaterial3D::shader_map;
Mutex BaseMaterial3D::shader_map_mutex;
BaseMaterial3D::ShaderNames *BaseMaterial3D::shader_names = nullptr;
Mutex BaseMaterial3D::material_mutex;
SelfList<BaseMaterial3D>::List BaseMaterial3D::dirty_materials;
HashMap<BaseMaterial3D::MaterialKey, BaseMaterial3D::ShaderData, BaseMaterial3D::MaterialKey> BaseMaterial3D::shader_map;
BaseMaterial3D::ShaderNames *BaseMaterial3D::shader_names = nullptr;
void BaseMaterial3D::init_shaders() {
shader_names = memnew(ShaderNames);
@ -623,10 +684,11 @@ void BaseMaterial3D::_update_shader() {
return; //no update required in the end
}
MutexLock lock(shader_map_mutex);
if (shader_map.has(current_key)) {
shader_map[current_key].users--;
if (shader_map[current_key].users == 0) {
//deallocate shader, as it's no longer in use
// Deallocate shader which is no longer in use.
RS::get_singleton()->free(shader_map[current_key].shader);
shader_map.erase(current_key);
}
@ -635,8 +697,13 @@ void BaseMaterial3D::_update_shader() {
current_key = mk;
if (shader_map.has(mk)) {
RS::get_singleton()->material_set_shader(_get_material(), shader_map[mk].shader);
shader_rid = shader_map[mk].shader;
shader_map[mk].users++;
if (_get_material().is_valid()) {
RS::get_singleton()->material_set_shader(_get_material(), shader_rid);
}
return;
}
@ -865,12 +932,10 @@ uniform float msdf_outline_size : hint_range(0.0, 250.0, 1.0);
// If alpha antialiasing isn't off, add in the edge variable.
if (alpha_antialiasing_mode != ALPHA_ANTIALIASING_OFF &&
(transparency == TRANSPARENCY_ALPHA_SCISSOR || transparency == TRANSPARENCY_ALPHA_HASH)) {
code += R"(
uniform float alpha_antialiasing_edge : hint_range(0.0, 1.0, 0.01);
uniform ivec2 albedo_texture_size;
)";
code += "uniform float alpha_antialiasing_edge : hint_range(0.0, 1.0, 0.01);\n";
}
code += "uniform ivec2 albedo_texture_size;\n";
code += "uniform float point_size : hint_range(0.1, 128.0, 0.1);\n";
if (!orm) {
@ -938,7 +1003,7 @@ uniform vec4 refraction_texture_channel;
code += "uniform sampler2D screen_texture : hint_screen_texture, repeat_disable, filter_linear_mipmap;\n";
}
if (proximity_fade_enabled) {
if (features[FEATURE_REFRACTION] || proximity_fade_enabled) {
code += "uniform sampler2D depth_texture : hint_depth_texture, repeat_disable, filter_nearest;\n";
}
@ -1448,7 +1513,7 @@ void fragment() {)";
vec3(1.0 + 0.055) * pow(albedo_tex.rgb, vec3(1.0 / 2.4)) - vec3(0.055),
vec3(12.92) * albedo_tex.rgb,
lessThan(albedo_tex.rgb, vec3(0.0031308)));
vec2 msdf_size = vec2(msdf_pixel_range) / vec2(textureSize(texture_albedo, 0));
vec2 msdf_size = vec2(msdf_pixel_range) / vec2(albedo_texture_size);
)";
if (flags[FLAG_USE_POINT_SIZE]) {
code += " vec2 dest_size = vec2(1.0) / fwidth(POINT_COORD);\n";
@ -1619,7 +1684,14 @@ void fragment() {)";
}
code += R"(
float ref_amount = 1.0 - albedo.a * albedo_tex.a;
EMISSION += textureLod(screen_texture, ref_ofs, ROUGHNESS * 8.0).rgb * ref_amount * EXPOSURE;
float refraction_depth_tex = textureLod(depth_texture, ref_ofs, 0.0).r;
vec4 refraction_view_pos = INV_PROJECTION_MATRIX * vec4(SCREEN_UV * 2.0 - 1.0, refraction_depth_tex, 1.0);
refraction_view_pos.xyz /= refraction_view_pos.w;
// If the depth buffer is lower then the model's Z position, use the refracted UV, otherwise use the normal screen UV.
// At low depth differences, decrease refraction intensity to avoid sudden discontinuities.
EMISSION += textureLod(screen_texture, mix(SCREEN_UV, ref_ofs, smoothstep(0.0, 1.0, VERTEX.z - refraction_view_pos.z)), ROUGHNESS * 8.0).rgb * ref_amount * EXPOSURE;
ALBEDO *= 1.0 - ref_amount;
// Force transparency on the material (required for refraction).
ALPHA = 1.0;
@ -1641,10 +1713,10 @@ void fragment() {)";
if (proximity_fade_enabled) {
code += R"(
// Proximity Fade: Enabled
float depth_tex = textureLod(depth_texture, SCREEN_UV, 0.0).r;
vec4 world_pos = INV_PROJECTION_MATRIX * vec4(SCREEN_UV * 2.0 - 1.0, depth_tex, 1.0);
world_pos.xyz /= world_pos.w;
ALPHA *= clamp(1.0 - smoothstep(world_pos.z + proximity_fade_distance, world_pos.z, VERTEX.z), 0.0, 1.0);
float proximity_depth_tex = textureLod(depth_texture, SCREEN_UV, 0.0).r;
vec4 proximity_view_pos = INV_PROJECTION_MATRIX * vec4(SCREEN_UV * 2.0 - 1.0, proximity_depth_tex, 1.0);
proximity_view_pos.xyz /= proximity_view_pos.w;
ALPHA *= clamp(1.0 - smoothstep(proximity_view_pos.z + proximity_fade_distance, proximity_view_pos.z, VERTEX.z), 0.0, 1.0);
)";
}
@ -1858,14 +1930,32 @@ void fragment() {)";
code += "}\n";
ShaderData shader_data;
shader_data.shader = RS::get_singleton()->shader_create();
shader_data.shader = RS::get_singleton()->shader_create_from_code(code);
shader_data.users = 1;
RS::get_singleton()->shader_set_code(shader_data.shader, code);
shader_map[mk] = shader_data;
shader_rid = shader_data.shader;
RS::get_singleton()->material_set_shader(_get_material(), shader_data.shader);
if (_get_material().is_valid()) {
RS::get_singleton()->material_set_shader(_get_material(), shader_rid);
}
}
void BaseMaterial3D::_check_material_rid() {
MutexLock lock(material_rid_mutex);
if (_get_material().is_null()) {
RID next_pass_rid;
if (get_next_pass().is_valid()) {
next_pass_rid = get_next_pass()->get_rid();
}
_set_material(RS::get_singleton()->material_create_from_shader(next_pass_rid, get_render_priority(), shader_rid));
for (KeyValue<StringName, Variant> param : pending_params) {
RS::get_singleton()->material_set_param(_get_material(), param.key, param.value);
}
pending_params.clear();
}
}
void BaseMaterial3D::flush_changes() {
@ -1878,17 +1968,28 @@ void BaseMaterial3D::flush_changes() {
}
void BaseMaterial3D::_queue_shader_change() {
if (!_is_initialized()) {
return;
}
MutexLock lock(material_mutex);
if (_is_initialized() && !element.in_list()) {
if (!element.in_list()) {
dirty_materials.add(&element);
}
}
void BaseMaterial3D::_material_set_param(const StringName &p_name, const Variant &p_value) {
if (_get_material().is_valid()) {
RS::get_singleton()->material_set_param(_get_material(), p_name, p_value);
} else {
pending_params[p_name] = p_value;
}
}
void BaseMaterial3D::set_albedo(const Color &p_albedo) {
albedo = p_albedo;
RS::get_singleton()->material_set_param(_get_material(), shader_names->albedo, p_albedo);
_material_set_param(shader_names->albedo, p_albedo);
}
Color BaseMaterial3D::get_albedo() const {
@ -1897,7 +1998,7 @@ Color BaseMaterial3D::get_albedo() const {
void BaseMaterial3D::set_specular(float p_specular) {
specular = p_specular;
RS::get_singleton()->material_set_param(_get_material(), shader_names->specular, p_specular);
_material_set_param(shader_names->specular, p_specular);
}
float BaseMaterial3D::get_specular() const {
@ -1906,7 +2007,7 @@ float BaseMaterial3D::get_specular() const {
void BaseMaterial3D::set_roughness(float p_roughness) {
roughness = p_roughness;
RS::get_singleton()->material_set_param(_get_material(), shader_names->roughness, p_roughness);
_material_set_param(shader_names->roughness, p_roughness);
}
float BaseMaterial3D::get_roughness() const {
@ -1915,7 +2016,7 @@ float BaseMaterial3D::get_roughness() const {
void BaseMaterial3D::set_metallic(float p_metallic) {
metallic = p_metallic;
RS::get_singleton()->material_set_param(_get_material(), shader_names->metallic, p_metallic);
_material_set_param(shader_names->metallic, p_metallic);
}
float BaseMaterial3D::get_metallic() const {
@ -1924,7 +2025,7 @@ float BaseMaterial3D::get_metallic() const {
void BaseMaterial3D::set_emission(const Color &p_emission) {
emission = p_emission;
RS::get_singleton()->material_set_param(_get_material(), shader_names->emission, p_emission);
_material_set_param(shader_names->emission, p_emission);
}
Color BaseMaterial3D::get_emission() const {
@ -1933,10 +2034,11 @@ Color BaseMaterial3D::get_emission() const {
void BaseMaterial3D::set_emission_energy_multiplier(float p_emission_energy_multiplier) {
emission_energy_multiplier = p_emission_energy_multiplier;
if (GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units")) {
RS::get_singleton()->material_set_param(_get_material(), shader_names->emission_energy, p_emission_energy_multiplier * emission_intensity);
_material_set_param(shader_names->emission_energy, p_emission_energy_multiplier * emission_intensity);
} else {
RS::get_singleton()->material_set_param(_get_material(), shader_names->emission_energy, p_emission_energy_multiplier);
_material_set_param(shader_names->emission_energy, p_emission_energy_multiplier);
}
}
@ -1947,7 +2049,7 @@ float BaseMaterial3D::get_emission_energy_multiplier() const {
void BaseMaterial3D::set_emission_intensity(float p_emission_intensity) {
ERR_FAIL_COND_EDMSG(!GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units"), "Cannot set material emission intensity when Physical Light Units disabled.");
emission_intensity = p_emission_intensity;
RS::get_singleton()->material_set_param(_get_material(), shader_names->emission_energy, emission_energy_multiplier * emission_intensity);
_material_set_param(shader_names->emission_energy, emission_energy_multiplier * emission_intensity);
}
float BaseMaterial3D::get_emission_intensity() const {
@ -1956,7 +2058,7 @@ float BaseMaterial3D::get_emission_intensity() const {
void BaseMaterial3D::set_normal_scale(float p_normal_scale) {
normal_scale = p_normal_scale;
RS::get_singleton()->material_set_param(_get_material(), shader_names->normal_scale, p_normal_scale);
_material_set_param(shader_names->normal_scale, p_normal_scale);
}
float BaseMaterial3D::get_normal_scale() const {
@ -1965,7 +2067,7 @@ float BaseMaterial3D::get_normal_scale() const {
void BaseMaterial3D::set_rim(float p_rim) {
rim = p_rim;
RS::get_singleton()->material_set_param(_get_material(), shader_names->rim, p_rim);
_material_set_param(shader_names->rim, p_rim);
}
float BaseMaterial3D::get_rim() const {
@ -1974,7 +2076,7 @@ float BaseMaterial3D::get_rim() const {
void BaseMaterial3D::set_rim_tint(float p_rim_tint) {
rim_tint = p_rim_tint;
RS::get_singleton()->material_set_param(_get_material(), shader_names->rim_tint, p_rim_tint);
_material_set_param(shader_names->rim_tint, p_rim_tint);
}
float BaseMaterial3D::get_rim_tint() const {
@ -1983,7 +2085,7 @@ float BaseMaterial3D::get_rim_tint() const {
void BaseMaterial3D::set_ao_light_affect(float p_ao_light_affect) {
ao_light_affect = p_ao_light_affect;
RS::get_singleton()->material_set_param(_get_material(), shader_names->ao_light_affect, p_ao_light_affect);
_material_set_param(shader_names->ao_light_affect, p_ao_light_affect);
}
float BaseMaterial3D::get_ao_light_affect() const {
@ -1992,7 +2094,7 @@ float BaseMaterial3D::get_ao_light_affect() const {
void BaseMaterial3D::set_clearcoat(float p_clearcoat) {
clearcoat = p_clearcoat;
RS::get_singleton()->material_set_param(_get_material(), shader_names->clearcoat, p_clearcoat);
_material_set_param(shader_names->clearcoat, p_clearcoat);
}
float BaseMaterial3D::get_clearcoat() const {
@ -2001,7 +2103,7 @@ float BaseMaterial3D::get_clearcoat() const {
void BaseMaterial3D::set_clearcoat_roughness(float p_clearcoat_roughness) {
clearcoat_roughness = p_clearcoat_roughness;
RS::get_singleton()->material_set_param(_get_material(), shader_names->clearcoat_roughness, p_clearcoat_roughness);
_material_set_param(shader_names->clearcoat_roughness, p_clearcoat_roughness);
}
float BaseMaterial3D::get_clearcoat_roughness() const {
@ -2010,7 +2112,7 @@ float BaseMaterial3D::get_clearcoat_roughness() const {
void BaseMaterial3D::set_anisotropy(float p_anisotropy) {
anisotropy = p_anisotropy;
RS::get_singleton()->material_set_param(_get_material(), shader_names->anisotropy, p_anisotropy);
_material_set_param(shader_names->anisotropy, p_anisotropy);
}
float BaseMaterial3D::get_anisotropy() const {
@ -2019,7 +2121,7 @@ float BaseMaterial3D::get_anisotropy() const {
void BaseMaterial3D::set_heightmap_scale(float p_heightmap_scale) {
heightmap_scale = p_heightmap_scale;
RS::get_singleton()->material_set_param(_get_material(), shader_names->heightmap_scale, p_heightmap_scale);
_material_set_param(shader_names->heightmap_scale, p_heightmap_scale);
}
float BaseMaterial3D::get_heightmap_scale() const {
@ -2028,7 +2130,7 @@ float BaseMaterial3D::get_heightmap_scale() const {
void BaseMaterial3D::set_subsurface_scattering_strength(float p_subsurface_scattering_strength) {
subsurface_scattering_strength = p_subsurface_scattering_strength;
RS::get_singleton()->material_set_param(_get_material(), shader_names->subsurface_scattering_strength, subsurface_scattering_strength);
_material_set_param(shader_names->subsurface_scattering_strength, subsurface_scattering_strength);
}
float BaseMaterial3D::get_subsurface_scattering_strength() const {
@ -2037,7 +2139,7 @@ float BaseMaterial3D::get_subsurface_scattering_strength() const {
void BaseMaterial3D::set_transmittance_color(const Color &p_color) {
transmittance_color = p_color;
RS::get_singleton()->material_set_param(_get_material(), shader_names->transmittance_color, p_color);
_material_set_param(shader_names->transmittance_color, p_color);
}
Color BaseMaterial3D::get_transmittance_color() const {
@ -2046,7 +2148,7 @@ Color BaseMaterial3D::get_transmittance_color() const {
void BaseMaterial3D::set_transmittance_depth(float p_depth) {
transmittance_depth = p_depth;
RS::get_singleton()->material_set_param(_get_material(), shader_names->transmittance_depth, p_depth);
_material_set_param(shader_names->transmittance_depth, p_depth);
}
float BaseMaterial3D::get_transmittance_depth() const {
@ -2055,7 +2157,7 @@ float BaseMaterial3D::get_transmittance_depth() const {
void BaseMaterial3D::set_transmittance_boost(float p_boost) {
transmittance_boost = p_boost;
RS::get_singleton()->material_set_param(_get_material(), shader_names->transmittance_boost, p_boost);
_material_set_param(shader_names->transmittance_boost, p_boost);
}
float BaseMaterial3D::get_transmittance_boost() const {
@ -2064,7 +2166,7 @@ float BaseMaterial3D::get_transmittance_boost() const {
void BaseMaterial3D::set_backlight(const Color &p_backlight) {
backlight = p_backlight;
RS::get_singleton()->material_set_param(_get_material(), shader_names->backlight, backlight);
_material_set_param(shader_names->backlight, backlight);
}
Color BaseMaterial3D::get_backlight() const {
@ -2073,7 +2175,7 @@ Color BaseMaterial3D::get_backlight() const {
void BaseMaterial3D::set_refraction(float p_refraction) {
refraction = p_refraction;
RS::get_singleton()->material_set_param(_get_material(), shader_names->refraction, refraction);
_material_set_param(shader_names->refraction, refraction);
}
float BaseMaterial3D::get_refraction() const {
@ -2261,11 +2363,10 @@ void BaseMaterial3D::set_texture(TextureParam p_param, const Ref<Texture2D> &p_t
textures[p_param] = p_texture;
Variant rid = p_texture.is_valid() ? Variant(p_texture->get_rid()) : Variant();
RS::get_singleton()->material_set_param(_get_material(), shader_names->texture_names[p_param], rid);
_material_set_param(shader_names->texture_names[p_param], rid);
if (p_texture.is_valid() && p_param == TEXTURE_ALBEDO) {
RS::get_singleton()->material_set_param(_get_material(), shader_names->albedo_texture_size,
Vector2i(p_texture->get_width(), p_texture->get_height()));
_material_set_param(shader_names->albedo_texture_size, Vector2i(p_texture->get_width(), p_texture->get_height()));
}
notify_property_list_changed();
@ -2461,7 +2562,7 @@ void BaseMaterial3D::_validate_property(PropertyInfo &p_property) const {
void BaseMaterial3D::set_point_size(float p_point_size) {
point_size = p_point_size;
RS::get_singleton()->material_set_param(_get_material(), shader_names->point_size, p_point_size);
_material_set_param(shader_names->point_size, p_point_size);
}
float BaseMaterial3D::get_point_size() const {
@ -2470,7 +2571,7 @@ float BaseMaterial3D::get_point_size() const {
void BaseMaterial3D::set_uv1_scale(const Vector3 &p_scale) {
uv1_scale = p_scale;
RS::get_singleton()->material_set_param(_get_material(), shader_names->uv1_scale, p_scale);
_material_set_param(shader_names->uv1_scale, p_scale);
}
Vector3 BaseMaterial3D::get_uv1_scale() const {
@ -2479,7 +2580,7 @@ Vector3 BaseMaterial3D::get_uv1_scale() const {
void BaseMaterial3D::set_uv1_offset(const Vector3 &p_offset) {
uv1_offset = p_offset;
RS::get_singleton()->material_set_param(_get_material(), shader_names->uv1_offset, p_offset);
_material_set_param(shader_names->uv1_offset, p_offset);
}
Vector3 BaseMaterial3D::get_uv1_offset() const {
@ -2489,7 +2590,7 @@ Vector3 BaseMaterial3D::get_uv1_offset() const {
void BaseMaterial3D::set_uv1_triplanar_blend_sharpness(float p_sharpness) {
// Negative values or values higher than 150 can result in NaNs, leading to broken rendering.
uv1_triplanar_sharpness = CLAMP(p_sharpness, 0.0, 150.0);
RS::get_singleton()->material_set_param(_get_material(), shader_names->uv1_blend_sharpness, uv1_triplanar_sharpness);
_material_set_param(shader_names->uv1_blend_sharpness, uv1_triplanar_sharpness);
}
float BaseMaterial3D::get_uv1_triplanar_blend_sharpness() const {
@ -2498,7 +2599,7 @@ float BaseMaterial3D::get_uv1_triplanar_blend_sharpness() const {
void BaseMaterial3D::set_uv2_scale(const Vector3 &p_scale) {
uv2_scale = p_scale;
RS::get_singleton()->material_set_param(_get_material(), shader_names->uv2_scale, p_scale);
_material_set_param(shader_names->uv2_scale, p_scale);
}
Vector3 BaseMaterial3D::get_uv2_scale() const {
@ -2507,7 +2608,7 @@ Vector3 BaseMaterial3D::get_uv2_scale() const {
void BaseMaterial3D::set_uv2_offset(const Vector3 &p_offset) {
uv2_offset = p_offset;
RS::get_singleton()->material_set_param(_get_material(), shader_names->uv2_offset, p_offset);
_material_set_param(shader_names->uv2_offset, p_offset);
}
Vector3 BaseMaterial3D::get_uv2_offset() const {
@ -2517,7 +2618,7 @@ Vector3 BaseMaterial3D::get_uv2_offset() const {
void BaseMaterial3D::set_uv2_triplanar_blend_sharpness(float p_sharpness) {
// Negative values or values higher than 150 can result in NaNs, leading to broken rendering.
uv2_triplanar_sharpness = CLAMP(p_sharpness, 0.0, 150.0);
RS::get_singleton()->material_set_param(_get_material(), shader_names->uv2_blend_sharpness, uv2_triplanar_sharpness);
_material_set_param(shader_names->uv2_blend_sharpness, uv2_triplanar_sharpness);
}
float BaseMaterial3D::get_uv2_triplanar_blend_sharpness() const {
@ -2536,7 +2637,7 @@ BaseMaterial3D::BillboardMode BaseMaterial3D::get_billboard_mode() const {
void BaseMaterial3D::set_particles_anim_h_frames(int p_frames) {
particles_anim_h_frames = p_frames;
RS::get_singleton()->material_set_param(_get_material(), shader_names->particles_anim_h_frames, p_frames);
_material_set_param(shader_names->particles_anim_h_frames, p_frames);
}
int BaseMaterial3D::get_particles_anim_h_frames() const {
@ -2545,7 +2646,7 @@ int BaseMaterial3D::get_particles_anim_h_frames() const {
void BaseMaterial3D::set_particles_anim_v_frames(int p_frames) {
particles_anim_v_frames = p_frames;
RS::get_singleton()->material_set_param(_get_material(), shader_names->particles_anim_v_frames, p_frames);
_material_set_param(shader_names->particles_anim_v_frames, p_frames);
}
int BaseMaterial3D::get_particles_anim_v_frames() const {
@ -2554,7 +2655,7 @@ int BaseMaterial3D::get_particles_anim_v_frames() const {
void BaseMaterial3D::set_particles_anim_loop(bool p_loop) {
particles_anim_loop = p_loop;
RS::get_singleton()->material_set_param(_get_material(), shader_names->particles_anim_loop, particles_anim_loop);
_material_set_param(shader_names->particles_anim_loop, particles_anim_loop);
}
bool BaseMaterial3D::get_particles_anim_loop() const {
@ -2573,7 +2674,7 @@ bool BaseMaterial3D::is_heightmap_deep_parallax_enabled() const {
void BaseMaterial3D::set_heightmap_deep_parallax_min_layers(int p_layer) {
deep_parallax_min_layers = p_layer;
RS::get_singleton()->material_set_param(_get_material(), shader_names->heightmap_min_layers, p_layer);
_material_set_param(shader_names->heightmap_min_layers, p_layer);
}
int BaseMaterial3D::get_heightmap_deep_parallax_min_layers() const {
@ -2582,7 +2683,7 @@ int BaseMaterial3D::get_heightmap_deep_parallax_min_layers() const {
void BaseMaterial3D::set_heightmap_deep_parallax_max_layers(int p_layer) {
deep_parallax_max_layers = p_layer;
RS::get_singleton()->material_set_param(_get_material(), shader_names->heightmap_max_layers, p_layer);
_material_set_param(shader_names->heightmap_max_layers, p_layer);
}
int BaseMaterial3D::get_heightmap_deep_parallax_max_layers() const {
@ -2591,7 +2692,7 @@ int BaseMaterial3D::get_heightmap_deep_parallax_max_layers() const {
void BaseMaterial3D::set_heightmap_deep_parallax_flip_tangent(bool p_flip) {
heightmap_parallax_flip_tangent = p_flip;
RS::get_singleton()->material_set_param(_get_material(), shader_names->heightmap_flip, Vector2(heightmap_parallax_flip_tangent ? -1 : 1, heightmap_parallax_flip_binormal ? -1 : 1));
_material_set_param(shader_names->heightmap_flip, Vector2(heightmap_parallax_flip_tangent ? -1 : 1, heightmap_parallax_flip_binormal ? -1 : 1));
}
bool BaseMaterial3D::get_heightmap_deep_parallax_flip_tangent() const {
@ -2600,7 +2701,7 @@ bool BaseMaterial3D::get_heightmap_deep_parallax_flip_tangent() const {
void BaseMaterial3D::set_heightmap_deep_parallax_flip_binormal(bool p_flip) {
heightmap_parallax_flip_binormal = p_flip;
RS::get_singleton()->material_set_param(_get_material(), shader_names->heightmap_flip, Vector2(heightmap_parallax_flip_tangent ? -1 : 1, heightmap_parallax_flip_binormal ? -1 : 1));
_material_set_param(shader_names->heightmap_flip, Vector2(heightmap_parallax_flip_tangent ? -1 : 1, heightmap_parallax_flip_binormal ? -1 : 1));
}
bool BaseMaterial3D::get_heightmap_deep_parallax_flip_binormal() const {
@ -2619,7 +2720,7 @@ bool BaseMaterial3D::is_grow_enabled() const {
void BaseMaterial3D::set_alpha_scissor_threshold(float p_threshold) {
alpha_scissor_threshold = p_threshold;
RS::get_singleton()->material_set_param(_get_material(), shader_names->alpha_scissor_threshold, p_threshold);
_material_set_param(shader_names->alpha_scissor_threshold, p_threshold);
}
float BaseMaterial3D::get_alpha_scissor_threshold() const {
@ -2628,7 +2729,7 @@ float BaseMaterial3D::get_alpha_scissor_threshold() const {
void BaseMaterial3D::set_alpha_hash_scale(float p_scale) {
alpha_hash_scale = p_scale;
RS::get_singleton()->material_set_param(_get_material(), shader_names->alpha_hash_scale, p_scale);
_material_set_param(shader_names->alpha_hash_scale, p_scale);
}
float BaseMaterial3D::get_alpha_hash_scale() const {
@ -2637,7 +2738,7 @@ float BaseMaterial3D::get_alpha_hash_scale() const {
void BaseMaterial3D::set_alpha_antialiasing_edge(float p_edge) {
alpha_antialiasing_edge = p_edge;
RS::get_singleton()->material_set_param(_get_material(), shader_names->alpha_antialiasing_edge, p_edge);
_material_set_param(shader_names->alpha_antialiasing_edge, p_edge);
}
float BaseMaterial3D::get_alpha_antialiasing_edge() const {
@ -2646,20 +2747,20 @@ float BaseMaterial3D::get_alpha_antialiasing_edge() const {
void BaseMaterial3D::set_grow(float p_grow) {
grow = p_grow;
RS::get_singleton()->material_set_param(_get_material(), shader_names->grow, p_grow);
_material_set_param(shader_names->grow, p_grow);
}
float BaseMaterial3D::get_grow() const {
return grow;
}
static Plane _get_texture_mask(BaseMaterial3D::TextureChannel p_channel) {
static const Plane masks[5] = {
Plane(1, 0, 0, 0),
Plane(0, 1, 0, 0),
Plane(0, 0, 1, 0),
Plane(0, 0, 0, 1),
Plane(0.3333333, 0.3333333, 0.3333333, 0),
static Vector4 _get_texture_mask(BaseMaterial3D::TextureChannel p_channel) {
static const Vector4 masks[5] = {
Vector4(1, 0, 0, 0),
Vector4(0, 1, 0, 0),
Vector4(0, 0, 1, 0),
Vector4(0, 0, 0, 1),
Vector4(0.3333333, 0.3333333, 0.3333333, 0),
};
return masks[p_channel];
@ -2668,7 +2769,7 @@ static Plane _get_texture_mask(BaseMaterial3D::TextureChannel p_channel) {
void BaseMaterial3D::set_metallic_texture_channel(TextureChannel p_channel) {
ERR_FAIL_INDEX(p_channel, 5);
metallic_texture_channel = p_channel;
RS::get_singleton()->material_set_param(_get_material(), shader_names->metallic_texture_channel, _get_texture_mask(p_channel));
_material_set_param(shader_names->metallic_texture_channel, _get_texture_mask(p_channel));
}
BaseMaterial3D::TextureChannel BaseMaterial3D::get_metallic_texture_channel() const {
@ -2688,7 +2789,7 @@ BaseMaterial3D::TextureChannel BaseMaterial3D::get_roughness_texture_channel() c
void BaseMaterial3D::set_ao_texture_channel(TextureChannel p_channel) {
ERR_FAIL_INDEX(p_channel, 5);
ao_texture_channel = p_channel;
RS::get_singleton()->material_set_param(_get_material(), shader_names->ao_texture_channel, _get_texture_mask(p_channel));
_material_set_param(shader_names->ao_texture_channel, _get_texture_mask(p_channel));
}
BaseMaterial3D::TextureChannel BaseMaterial3D::get_ao_texture_channel() const {
@ -2698,7 +2799,7 @@ BaseMaterial3D::TextureChannel BaseMaterial3D::get_ao_texture_channel() const {
void BaseMaterial3D::set_refraction_texture_channel(TextureChannel p_channel) {
ERR_FAIL_INDEX(p_channel, 5);
refraction_texture_channel = p_channel;
RS::get_singleton()->material_set_param(_get_material(), shader_names->refraction_texture_channel, _get_texture_mask(p_channel));
_material_set_param(shader_names->refraction_texture_channel, _get_texture_mask(p_channel));
}
BaseMaterial3D::TextureChannel BaseMaterial3D::get_refraction_texture_channel() const {
@ -2770,7 +2871,7 @@ bool BaseMaterial3D::is_proximity_fade_enabled() const {
void BaseMaterial3D::set_proximity_fade_distance(float p_distance) {
proximity_fade_distance = MAX(p_distance, 0.01);
RS::get_singleton()->material_set_param(_get_material(), shader_names->proximity_fade_distance, proximity_fade_distance);
_material_set_param(shader_names->proximity_fade_distance, proximity_fade_distance);
}
float BaseMaterial3D::get_proximity_fade_distance() const {
@ -2779,7 +2880,7 @@ float BaseMaterial3D::get_proximity_fade_distance() const {
void BaseMaterial3D::set_msdf_pixel_range(float p_range) {
msdf_pixel_range = p_range;
RS::get_singleton()->material_set_param(_get_material(), shader_names->msdf_pixel_range, p_range);
_material_set_param(shader_names->msdf_pixel_range, p_range);
}
float BaseMaterial3D::get_msdf_pixel_range() const {
@ -2788,7 +2889,7 @@ float BaseMaterial3D::get_msdf_pixel_range() const {
void BaseMaterial3D::set_msdf_outline_size(float p_size) {
msdf_outline_size = p_size;
RS::get_singleton()->material_set_param(_get_material(), shader_names->msdf_outline_size, p_size);
_material_set_param(shader_names->msdf_outline_size, p_size);
}
float BaseMaterial3D::get_msdf_outline_size() const {
@ -2807,7 +2908,7 @@ BaseMaterial3D::DistanceFadeMode BaseMaterial3D::get_distance_fade() const {
void BaseMaterial3D::set_distance_fade_max_distance(float p_distance) {
distance_fade_max_distance = p_distance;
RS::get_singleton()->material_set_param(_get_material(), shader_names->distance_fade_max, distance_fade_max_distance);
_material_set_param(shader_names->distance_fade_max, distance_fade_max_distance);
}
float BaseMaterial3D::get_distance_fade_max_distance() const {
@ -2816,7 +2917,7 @@ float BaseMaterial3D::get_distance_fade_max_distance() const {
void BaseMaterial3D::set_distance_fade_min_distance(float p_distance) {
distance_fade_min_distance = p_distance;
RS::get_singleton()->material_set_param(_get_material(), shader_names->distance_fade_min, distance_fade_min_distance);
_material_set_param(shader_names->distance_fade_min, distance_fade_min_distance);
}
float BaseMaterial3D::get_distance_fade_min_distance() const {
@ -2835,13 +2936,15 @@ BaseMaterial3D::EmissionOperator BaseMaterial3D::get_emission_operator() const {
return emission_op;
}
RID BaseMaterial3D::get_rid() const {
const_cast<BaseMaterial3D *>(this)->_update_shader();
const_cast<BaseMaterial3D *>(this)->_check_material_rid();
return _get_material();
}
RID BaseMaterial3D::get_shader_rid() const {
MutexLock lock(material_mutex);
if (element.in_list()) {
((BaseMaterial3D *)this)->_update_shader();
}
ERR_FAIL_COND_V(!shader_map.has(current_key), RID());
return shader_map[current_key].shader;
const_cast<BaseMaterial3D *>(this)->_update_shader();
return shader_rid;
}
Shader::Mode BaseMaterial3D::get_shader_mode() const {
@ -3425,21 +3528,25 @@ BaseMaterial3D::BaseMaterial3D(bool p_orm) :
current_key.invalid_key = 1;
_mark_initialized(callable_mp(this, &BaseMaterial3D::_queue_shader_change), callable_mp(this, &BaseMaterial3D::_update_shader));
_mark_initialized(callable_mp(this, &BaseMaterial3D::_queue_shader_change), Callable());
}
BaseMaterial3D::~BaseMaterial3D() {
ERR_FAIL_NULL(RenderingServer::get_singleton());
MutexLock lock(material_mutex);
ERR_FAIL_NULL(RS::get_singleton());
if (shader_map.has(current_key)) {
shader_map[current_key].users--;
if (shader_map[current_key].users == 0) {
//deallocate shader, as it's no longer in use
RS::get_singleton()->free(shader_map[current_key].shader);
shader_map.erase(current_key);
{
MutexLock lock(shader_map_mutex);
if (shader_map.has(current_key)) {
shader_map[current_key].users--;
if (shader_map[current_key].users == 0) {
// Deallocate shader which is no longer in use.
RS::get_singleton()->free(shader_map[current_key].shader);
shader_map.erase(current_key);
}
}
}
if (_get_material().is_valid()) {
RS::get_singleton()->material_set_shader(_get_material(), RID());
}
}

View file

@ -42,7 +42,7 @@ class Material : public Resource {
RES_BASE_EXTENSION("material")
OBJ_SAVE_TYPE(Material);
RID material;
mutable RID material;
Ref<Material> next_pass;
int render_priority;
@ -55,6 +55,7 @@ class Material : public Resource {
void inspect_native_shader_code();
protected:
_FORCE_INLINE_ void _set_material(RID p_material) const { material = p_material; }
_FORCE_INLINE_ RID _get_material() const { return material; }
static void _bind_methods();
virtual bool _can_do_next_pass() const;
@ -66,8 +67,8 @@ protected:
void _mark_initialized(const Callable &p_add_to_dirty_list, const Callable &p_update_shader);
bool _is_initialized() { return init_state == INIT_STATE_READY; }
GDVIRTUAL0RC(RID, _get_shader_rid)
GDVIRTUAL0RC(Shader::Mode, _get_shader_mode)
GDVIRTUAL0RC_REQUIRED(RID, _get_shader_rid)
GDVIRTUAL0RC_REQUIRED(Shader::Mode, _get_shader_mode)
GDVIRTUAL0RC(bool, _can_do_next_pass)
GDVIRTUAL0RC(bool, _can_use_render_priority)
public:
@ -97,6 +98,7 @@ class ShaderMaterial : public Material {
mutable HashMap<StringName, StringName> remap_cache;
mutable HashMap<StringName, Variant> param_cache;
mutable Mutex material_rid_mutex;
protected:
bool _set(const StringName &p_name, const Variant &p_value);
@ -115,6 +117,7 @@ protected:
virtual bool _can_use_render_priority() const override;
void _shader_changed();
void _check_material_rid() const;
public:
void set_shader(const Ref<Shader> &p_shader);
@ -125,6 +128,7 @@ public:
virtual Shader::Mode get_shader_mode() const override;
virtual RID get_rid() const override;
virtual RID get_shader_rid() const override;
ShaderMaterial();
@ -136,6 +140,9 @@ class StandardMaterial3D;
class BaseMaterial3D : public Material {
GDCLASS(BaseMaterial3D, Material);
private:
mutable Mutex material_rid_mutex;
public:
enum TextureParam {
TEXTURE_ALBEDO,
@ -361,6 +368,7 @@ private:
};
static HashMap<MaterialKey, ShaderData, MaterialKey> shader_map;
static Mutex shader_map_mutex;
MaterialKey current_key;
@ -467,8 +475,12 @@ private:
void _update_shader();
_FORCE_INLINE_ void _queue_shader_change();
void _check_material_rid();
void _material_set_param(const StringName &p_name, const Variant &p_value);
bool orm;
RID shader_rid;
HashMap<StringName, Variant> pending_params;
Color albedo;
float specular = 0.0f;
@ -775,6 +787,7 @@ public:
static Ref<Material> get_material_for_2d(bool p_shaded, Transparency p_transparency, bool p_double_sided, bool p_billboard = false, bool p_billboard_y = false, bool p_msdf = false, bool p_no_depth = false, bool p_fixed_size = false, TextureFilter p_filter = TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, AlphaAntiAliasing p_alpha_antialiasing_mode = ALPHA_ANTIALIASING_OFF, RID *r_shader_rid = nullptr);
virtual RID get_rid() const override;
virtual RID get_shader_rid() const override;
virtual Shader::Mode get_shader_mode() const override;

View file

@ -45,23 +45,23 @@ void MeshConvexDecompositionSettings::set_max_concavity(real_t p_max_concavity)
real_t MeshConvexDecompositionSettings::get_max_concavity() const {
return max_concavity;
};
}
void MeshConvexDecompositionSettings::set_symmetry_planes_clipping_bias(real_t p_symmetry_planes_clipping_bias) {
symmetry_planes_clipping_bias = CLAMP(p_symmetry_planes_clipping_bias, 0.0, 1.0);
};
}
real_t MeshConvexDecompositionSettings::get_symmetry_planes_clipping_bias() const {
return symmetry_planes_clipping_bias;
};
}
void MeshConvexDecompositionSettings::set_revolution_axes_clipping_bias(real_t p_revolution_axes_clipping_bias) {
revolution_axes_clipping_bias = CLAMP(p_revolution_axes_clipping_bias, 0.0, 1.0);
};
}
real_t MeshConvexDecompositionSettings::get_revolution_axes_clipping_bias() const {
return revolution_axes_clipping_bias;
};
}
void MeshConvexDecompositionSettings::set_min_volume_per_convex_hull(real_t p_min_volume_per_convex_hull) {
min_volume_per_convex_hull = CLAMP(p_min_volume_per_convex_hull, 0.0001, 0.01);
@ -205,81 +205,81 @@ Mesh::ConvexDecompositionFunc Mesh::convex_decomposition_function = nullptr;
int Mesh::get_surface_count() const {
int ret = 0;
GDVIRTUAL_REQUIRED_CALL(_get_surface_count, ret);
GDVIRTUAL_CALL(_get_surface_count, ret);
return ret;
}
int Mesh::surface_get_array_len(int p_idx) const {
int ret = 0;
GDVIRTUAL_REQUIRED_CALL(_surface_get_array_len, p_idx, ret);
GDVIRTUAL_CALL(_surface_get_array_len, p_idx, ret);
return ret;
}
int Mesh::surface_get_array_index_len(int p_idx) const {
int ret = 0;
GDVIRTUAL_REQUIRED_CALL(_surface_get_array_index_len, p_idx, ret);
GDVIRTUAL_CALL(_surface_get_array_index_len, p_idx, ret);
return ret;
}
Array Mesh::surface_get_arrays(int p_surface) const {
Array ret;
GDVIRTUAL_REQUIRED_CALL(_surface_get_arrays, p_surface, ret);
GDVIRTUAL_CALL(_surface_get_arrays, p_surface, ret);
return ret;
}
TypedArray<Array> Mesh::surface_get_blend_shape_arrays(int p_surface) const {
TypedArray<Array> ret;
GDVIRTUAL_REQUIRED_CALL(_surface_get_blend_shape_arrays, p_surface, ret);
GDVIRTUAL_CALL(_surface_get_blend_shape_arrays, p_surface, ret);
return ret;
}
Dictionary Mesh::surface_get_lods(int p_surface) const {
Dictionary ret;
GDVIRTUAL_REQUIRED_CALL(_surface_get_lods, p_surface, ret);
GDVIRTUAL_CALL(_surface_get_lods, p_surface, ret);
return ret;
}
BitField<Mesh::ArrayFormat> Mesh::surface_get_format(int p_idx) const {
uint32_t ret = 0;
GDVIRTUAL_REQUIRED_CALL(_surface_get_format, p_idx, ret);
GDVIRTUAL_CALL(_surface_get_format, p_idx, ret);
return ret;
}
Mesh::PrimitiveType Mesh::surface_get_primitive_type(int p_idx) const {
uint32_t ret = PRIMITIVE_MAX;
GDVIRTUAL_REQUIRED_CALL(_surface_get_primitive_type, p_idx, ret);
GDVIRTUAL_CALL(_surface_get_primitive_type, p_idx, ret);
return (Mesh::PrimitiveType)ret;
}
void Mesh::surface_set_material(int p_idx, const Ref<Material> &p_material) {
GDVIRTUAL_REQUIRED_CALL(_surface_set_material, p_idx, p_material);
GDVIRTUAL_CALL(_surface_set_material, p_idx, p_material);
}
Ref<Material> Mesh::surface_get_material(int p_idx) const {
Ref<Material> ret;
GDVIRTUAL_REQUIRED_CALL(_surface_get_material, p_idx, ret);
GDVIRTUAL_CALL(_surface_get_material, p_idx, ret);
return ret;
}
int Mesh::get_blend_shape_count() const {
int ret = 0;
GDVIRTUAL_REQUIRED_CALL(_get_blend_shape_count, ret);
GDVIRTUAL_CALL(_get_blend_shape_count, ret);
return ret;
}
StringName Mesh::get_blend_shape_name(int p_index) const {
StringName ret;
GDVIRTUAL_REQUIRED_CALL(_get_blend_shape_name, p_index, ret);
GDVIRTUAL_CALL(_get_blend_shape_name, p_index, ret);
return ret;
}
void Mesh::set_blend_shape_name(int p_index, const StringName &p_name) {
GDVIRTUAL_REQUIRED_CALL(_set_blend_shape_name, p_index, p_name);
GDVIRTUAL_CALL(_set_blend_shape_name, p_index, p_name);
}
AABB Mesh::get_aabb() const {
AABB ret;
GDVIRTUAL_REQUIRED_CALL(_get_aabb, ret);
GDVIRTUAL_CALL(_get_aabb, ret);
return ret;
}
@ -385,7 +385,7 @@ Ref<TriangleMesh> Mesh::generate_triangle_mesh() const {
}
}
triangle_mesh = Ref<TriangleMesh>(memnew(TriangleMesh));
triangle_mesh.instantiate();
triangle_mesh->create(faces);
return triangle_mesh;
@ -905,7 +905,7 @@ Vector<Ref<Shape3D>> Mesh::convex_decompose(const Ref<MeshConvexDecompositionSet
ERR_FAIL_NULL_V(convex_decomposition_function, Vector<Ref<Shape3D>>());
Ref<TriangleMesh> tm = generate_triangle_mesh();
ERR_FAIL_COND_V(!tm.is_valid(), Vector<Ref<Shape3D>>());
ERR_FAIL_COND_V(tm.is_null(), Vector<Ref<Shape3D>>());
const Vector<TriangleMesh::Triangle> &triangles = tm->get_triangles();
int triangle_count = triangles.size();
@ -1315,7 +1315,7 @@ bool ArrayMesh::_set(const StringName &p_name, const Variant &p_value) {
String sname = p_name;
if (sname.begins_with("surface_")) {
int sl = sname.find("/");
int sl = sname.find_char('/');
if (sl == -1) {
return false;
}
@ -1708,7 +1708,7 @@ bool ArrayMesh::_get(const StringName &p_name, Variant &r_ret) const {
String sname = p_name;
if (sname.begins_with("surface_")) {
int sl = sname.find("/");
int sl = sname.find_char('/');
if (sl == -1) {
return false;
}
@ -1740,7 +1740,7 @@ void ArrayMesh::_get_property_list(List<PropertyInfo> *p_list) const {
}
for (int i = 0; i < surfaces.size(); i++) {
p_list->push_back(PropertyInfo(Variant::STRING, "surface_" + itos(i) + "/name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR));
p_list->push_back(PropertyInfo(Variant::STRING, "surface_" + itos(i) + "/name", PROPERTY_HINT_NO_NODEPATH, "", PROPERTY_USAGE_EDITOR));
if (surfaces[i].is_2d) {
p_list->push_back(PropertyInfo(Variant::OBJECT, "surface_" + itos(i) + "/material", PROPERTY_HINT_RESOURCE_TYPE, "CanvasItemMaterial,ShaderMaterial", PROPERTY_USAGE_EDITOR));
} else {
@ -2007,6 +2007,17 @@ void ArrayMesh::clear_surfaces() {
aabb = AABB();
}
void ArrayMesh::surface_remove(int p_surface) {
ERR_FAIL_INDEX(p_surface, surfaces.size());
RS::get_singleton()->mesh_surface_remove(mesh, p_surface);
surfaces.remove_at(p_surface);
clear_cache();
_recompute_aabb();
notify_property_list_changed();
emit_changed();
}
void ArrayMesh::set_custom_aabb(const AABB &p_custom) {
_create_if_empty();
custom_aabb = p_custom;
@ -2251,6 +2262,7 @@ Error ArrayMesh::lightmap_unwrap_cached(const Transform3D &p_base_transform, flo
}
void ArrayMesh::set_shadow_mesh(const Ref<ArrayMesh> &p_mesh) {
ERR_FAIL_COND_MSG(p_mesh == this, "Cannot set a mesh as its own shadow mesh.");
shadow_mesh = p_mesh;
if (shadow_mesh.is_valid()) {
RS::get_singleton()->mesh_set_shadow_mesh(mesh, shadow_mesh->get_rid());
@ -2274,6 +2286,7 @@ void ArrayMesh::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_surface_from_arrays", "primitive", "arrays", "blend_shapes", "lods", "flags"), &ArrayMesh::add_surface_from_arrays, DEFVAL(Array()), DEFVAL(Dictionary()), DEFVAL(0));
ClassDB::bind_method(D_METHOD("clear_surfaces"), &ArrayMesh::clear_surfaces);
ClassDB::bind_method(D_METHOD("surface_remove", "surf_idx"), &ArrayMesh::surface_remove);
ClassDB::bind_method(D_METHOD("surface_update_vertex_region", "surf_idx", "offset", "data"), &ArrayMesh::surface_update_vertex_region);
ClassDB::bind_method(D_METHOD("surface_update_attribute_region", "surf_idx", "offset", "data"), &ArrayMesh::surface_update_attribute_region);
ClassDB::bind_method(D_METHOD("surface_update_skin_region", "surf_idx", "offset", "data"), &ArrayMesh::surface_update_skin_region);
@ -2307,10 +2320,10 @@ void ArrayMesh::_bind_methods() {
ClassDB::bind_method(D_METHOD("_set_surfaces", "surfaces"), &ArrayMesh::_set_surfaces);
ClassDB::bind_method(D_METHOD("_get_surfaces"), &ArrayMesh::_get_surfaces);
ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "_blend_shape_names", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_blend_shape_names", "_get_blend_shape_names");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "_surfaces", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_surfaces", "_get_surfaces");
ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "_blend_shape_names", PROPERTY_HINT_NO_NODEPATH, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_blend_shape_names", "_get_blend_shape_names");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "_surfaces", PROPERTY_HINT_NO_NODEPATH, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_surfaces", "_get_surfaces");
ADD_PROPERTY(PropertyInfo(Variant::INT, "blend_shape_mode", PROPERTY_HINT_ENUM, "Normalized,Relative"), "set_blend_shape_mode", "get_blend_shape_mode");
ADD_PROPERTY(PropertyInfo(Variant::AABB, "custom_aabb", PROPERTY_HINT_NONE, "suffix:m"), "set_custom_aabb", "get_custom_aabb");
ADD_PROPERTY(PropertyInfo(Variant::AABB, "custom_aabb", PROPERTY_HINT_NO_NODEPATH, "suffix:m"), "set_custom_aabb", "get_custom_aabb");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shadow_mesh", PROPERTY_HINT_RESOURCE_TYPE, "ArrayMesh"), "set_shadow_mesh", "get_shadow_mesh");
}

View file

@ -68,20 +68,20 @@ public:
protected:
static void _bind_methods();
GDVIRTUAL0RC(int, _get_surface_count)
GDVIRTUAL1RC(int, _surface_get_array_len, int)
GDVIRTUAL1RC(int, _surface_get_array_index_len, int)
GDVIRTUAL1RC(Array, _surface_get_arrays, int)
GDVIRTUAL1RC(TypedArray<Array>, _surface_get_blend_shape_arrays, int)
GDVIRTUAL1RC(Dictionary, _surface_get_lods, int)
GDVIRTUAL1RC(uint32_t, _surface_get_format, int)
GDVIRTUAL1RC(uint32_t, _surface_get_primitive_type, int)
GDVIRTUAL2(_surface_set_material, int, Ref<Material>)
GDVIRTUAL1RC(Ref<Material>, _surface_get_material, int)
GDVIRTUAL0RC(int, _get_blend_shape_count)
GDVIRTUAL1RC(StringName, _get_blend_shape_name, int)
GDVIRTUAL2(_set_blend_shape_name, int, StringName)
GDVIRTUAL0RC(AABB, _get_aabb)
GDVIRTUAL0RC_REQUIRED(int, _get_surface_count)
GDVIRTUAL1RC_REQUIRED(int, _surface_get_array_len, int)
GDVIRTUAL1RC_REQUIRED(int, _surface_get_array_index_len, int)
GDVIRTUAL1RC_REQUIRED(Array, _surface_get_arrays, int)
GDVIRTUAL1RC_REQUIRED(TypedArray<Array>, _surface_get_blend_shape_arrays, int)
GDVIRTUAL1RC_REQUIRED(Dictionary, _surface_get_lods, int)
GDVIRTUAL1RC_REQUIRED(uint32_t, _surface_get_format, int)
GDVIRTUAL1RC_REQUIRED(uint32_t, _surface_get_primitive_type, int)
GDVIRTUAL2_REQUIRED(_surface_set_material, int, Ref<Material>)
GDVIRTUAL1RC_REQUIRED(Ref<Material>, _surface_get_material, int)
GDVIRTUAL0RC_REQUIRED(int, _get_blend_shape_count)
GDVIRTUAL1RC_REQUIRED(StringName, _get_blend_shape_name, int)
GDVIRTUAL2_REQUIRED(_set_blend_shape_name, int, StringName)
GDVIRTUAL0RC_REQUIRED(AABB, _get_aabb)
public:
enum {
@ -362,6 +362,7 @@ public:
int get_surface_count() const override;
void surface_remove(int p_surface);
void clear_surfaces();
void surface_set_custom_aabb(int p_idx, const AABB &p_aabb); //only recognized by driver

View file

@ -195,6 +195,15 @@ Vector<Color> MultiMesh::_get_custom_data_array() const {
#endif // DISABLE_DEPRECATED
void MultiMesh::set_buffer(const Vector<float> &p_buffer) {
if (instance_count == 0) {
return;
}
uint32_t stride = transform_format == TRANSFORM_2D ? 8 : 12;
stride += use_colors ? 4 : 0;
stride += use_custom_data ? 4 : 0;
ERR_FAIL_COND_MSG(stride * instance_count != p_buffer.size(), "Cannot set a buffer on a Multimesh that is a different size from the Multimesh's existing buffer.");
RS::get_singleton()->multimesh_set_buffer(multimesh, p_buffer);
}
@ -202,9 +211,13 @@ Vector<float> MultiMesh::get_buffer() const {
return RS::get_singleton()->multimesh_get_buffer(multimesh);
}
void MultiMesh::set_buffer_interpolated(const Vector<float> &p_buffer_curr, const Vector<float> &p_buffer_prev) {
RS::get_singleton()->multimesh_set_buffer_interpolated(multimesh, p_buffer_curr, p_buffer_prev);
}
void MultiMesh::set_mesh(const Ref<Mesh> &p_mesh) {
mesh = p_mesh;
if (!mesh.is_null()) {
if (mesh.is_valid()) {
RenderingServer::get_singleton()->multimesh_set_mesh(multimesh, mesh->get_rid());
} else {
RenderingServer::get_singleton()->multimesh_set_mesh(multimesh, RID());
@ -236,39 +249,69 @@ int MultiMesh::get_visible_instance_count() const {
return visible_instance_count;
}
void MultiMesh::set_physics_interpolation_quality(PhysicsInterpolationQuality p_quality) {
_physics_interpolation_quality = p_quality;
RenderingServer::get_singleton()->multimesh_set_physics_interpolation_quality(multimesh, (RS::MultimeshPhysicsInterpolationQuality)p_quality);
}
void MultiMesh::set_instance_transform(int p_instance, const Transform3D &p_transform) {
ERR_FAIL_INDEX_MSG(p_instance, instance_count, "Instance index out of bounds. Instance index must be less than `instance_count` and greater than or equal to zero.");
ERR_FAIL_COND_MSG(transform_format == TRANSFORM_2D, "Can't set Transform3D on a Multimesh configured to use Transform2D. Ensure that you have set the `transform_format` to `TRANSFORM_3D`.");
RenderingServer::get_singleton()->multimesh_instance_set_transform(multimesh, p_instance, p_transform);
}
void MultiMesh::set_instance_transform_2d(int p_instance, const Transform2D &p_transform) {
ERR_FAIL_INDEX_MSG(p_instance, instance_count, "Instance index out of bounds. Instance index must be less than `instance_count` and greater than or equal to zero.");
ERR_FAIL_COND_MSG(transform_format == TRANSFORM_3D, "Can't set Transform2D on a Multimesh configured to use Transform3D. Ensure that you have set the `transform_format` to `TRANSFORM_2D`.");
RenderingServer::get_singleton()->multimesh_instance_set_transform_2d(multimesh, p_instance, p_transform);
emit_changed();
}
Transform3D MultiMesh::get_instance_transform(int p_instance) const {
ERR_FAIL_INDEX_V_MSG(p_instance, instance_count, Transform3D(), "Instance index out of bounds. Instance index must be less than `instance_count` and greater than or equal to zero.");
ERR_FAIL_COND_V_MSG(transform_format == TRANSFORM_2D, Transform3D(), "Can't get Transform3D on a Multimesh configured to use Transform2D. Ensure that you have set the `transform_format` to `TRANSFORM_3D`.");
return RenderingServer::get_singleton()->multimesh_instance_get_transform(multimesh, p_instance);
}
Transform2D MultiMesh::get_instance_transform_2d(int p_instance) const {
ERR_FAIL_INDEX_V_MSG(p_instance, instance_count, Transform2D(), "Instance index out of bounds. Instance index must be less than `instance_count` and greater than or equal to zero.");
ERR_FAIL_COND_V_MSG(transform_format == TRANSFORM_3D, Transform2D(), "Can't get Transform2D on a Multimesh configured to use Transform3D. Ensure that you have set the `transform_format` to `TRANSFORM_2D`.");
return RenderingServer::get_singleton()->multimesh_instance_get_transform_2d(multimesh, p_instance);
}
void MultiMesh::set_instance_color(int p_instance, const Color &p_color) {
ERR_FAIL_INDEX_MSG(p_instance, instance_count, "Instance index out of bounds. Instance index must be less than `instance_count` and greater than or equal to zero.");
ERR_FAIL_COND_MSG(!use_colors, "Can't set instance color on a Multimesh that isn't using colors. Ensure that you have `use_colors` property of this Multimesh set to `true`.");
RenderingServer::get_singleton()->multimesh_instance_set_color(multimesh, p_instance, p_color);
}
Color MultiMesh::get_instance_color(int p_instance) const {
ERR_FAIL_INDEX_V_MSG(p_instance, instance_count, Color(), "Instance index out of bounds. Instance index must be less than `instance_count` and greater than or equal to zero.");
ERR_FAIL_COND_V_MSG(!use_colors, Color(), "Can't get instance color on a Multimesh that isn't using colors. Ensure that you have `use_colors` property of this Multimesh set to `true`.");
return RenderingServer::get_singleton()->multimesh_instance_get_color(multimesh, p_instance);
}
void MultiMesh::set_instance_custom_data(int p_instance, const Color &p_custom_data) {
ERR_FAIL_INDEX_MSG(p_instance, instance_count, "Instance index out of bounds. Instance index must be less than `instance_count` and greater than or equal to zero.");
ERR_FAIL_COND_MSG(!use_custom_data, "Can't get instance custom data on a Multimesh that isn't using custom data. Ensure that you have `use_custom_data` property of this Multimesh set to `true`.");
RenderingServer::get_singleton()->multimesh_instance_set_custom_data(multimesh, p_instance, p_custom_data);
}
Color MultiMesh::get_instance_custom_data(int p_instance) const {
ERR_FAIL_INDEX_V_MSG(p_instance, instance_count, Color(), "Instance index out of bounds. Instance index must be less than `instance_count` and greater than or equal to zero.");
ERR_FAIL_COND_V_MSG(!use_custom_data, Color(), "Can't get instance custom data on a Multimesh that isn't using custom data. Ensure that you have `use_custom_data` property of this Multimesh set to `true`.");
return RenderingServer::get_singleton()->multimesh_instance_get_custom_data(multimesh, p_instance);
}
void MultiMesh::reset_instance_physics_interpolation(int p_instance) {
ERR_FAIL_INDEX_MSG(p_instance, instance_count, "Instance index out of bounds. Instance index must be less than `instance_count` and greater than or equal to zero.");
RenderingServer::get_singleton()->multimesh_instance_reset_physics_interpolation(multimesh, p_instance);
}
void MultiMesh::set_physics_interpolated(bool p_interpolated) {
RenderingServer::get_singleton()->multimesh_set_physics_interpolated(multimesh, p_interpolated);
}
void MultiMesh::set_custom_aabb(const AABB &p_custom) {
custom_aabb = p_custom;
RS::get_singleton()->multimesh_set_custom_aabb(multimesh, custom_aabb);
@ -288,7 +331,7 @@ RID MultiMesh::get_rid() const {
}
void MultiMesh::set_use_colors(bool p_enable) {
ERR_FAIL_COND(instance_count > 0);
ERR_FAIL_COND_MSG(instance_count > 0, "Instance count must be 0 to toggle whether colors are used.");
use_colors = p_enable;
}
@ -297,7 +340,7 @@ bool MultiMesh::is_using_colors() const {
}
void MultiMesh::set_use_custom_data(bool p_enable) {
ERR_FAIL_COND(instance_count > 0);
ERR_FAIL_COND_MSG(instance_count > 0, "Instance count must be 0 to toggle whether custom data is used.");
use_custom_data = p_enable;
}
@ -306,7 +349,7 @@ bool MultiMesh::is_using_custom_data() const {
}
void MultiMesh::set_transform_format(TransformFormat p_transform_format) {
ERR_FAIL_COND(instance_count > 0);
ERR_FAIL_COND_MSG(instance_count > 0, "Instance count must be 0 to change the transform format.");
transform_format = p_transform_format;
}
@ -328,6 +371,8 @@ void MultiMesh::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_instance_count"), &MultiMesh::get_instance_count);
ClassDB::bind_method(D_METHOD("set_visible_instance_count", "count"), &MultiMesh::set_visible_instance_count);
ClassDB::bind_method(D_METHOD("get_visible_instance_count"), &MultiMesh::get_visible_instance_count);
ClassDB::bind_method(D_METHOD("set_physics_interpolation_quality", "quality"), &MultiMesh::set_physics_interpolation_quality);
ClassDB::bind_method(D_METHOD("get_physics_interpolation_quality"), &MultiMesh::get_physics_interpolation_quality);
ClassDB::bind_method(D_METHOD("set_instance_transform", "instance", "transform"), &MultiMesh::set_instance_transform);
ClassDB::bind_method(D_METHOD("set_instance_transform_2d", "instance", "transform"), &MultiMesh::set_instance_transform_2d);
ClassDB::bind_method(D_METHOD("get_instance_transform", "instance"), &MultiMesh::get_instance_transform);
@ -336,6 +381,7 @@ void MultiMesh::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_instance_color", "instance"), &MultiMesh::get_instance_color);
ClassDB::bind_method(D_METHOD("set_instance_custom_data", "instance", "custom_data"), &MultiMesh::set_instance_custom_data);
ClassDB::bind_method(D_METHOD("get_instance_custom_data", "instance"), &MultiMesh::get_instance_custom_data);
ClassDB::bind_method(D_METHOD("reset_instance_physics_interpolation", "instance"), &MultiMesh::reset_instance_physics_interpolation);
ClassDB::bind_method(D_METHOD("set_custom_aabb", "aabb"), &MultiMesh::set_custom_aabb);
ClassDB::bind_method(D_METHOD("get_custom_aabb"), &MultiMesh::get_custom_aabb);
ClassDB::bind_method(D_METHOD("get_aabb"), &MultiMesh::get_aabb);
@ -343,6 +389,8 @@ void MultiMesh::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_buffer"), &MultiMesh::get_buffer);
ClassDB::bind_method(D_METHOD("set_buffer", "buffer"), &MultiMesh::set_buffer);
ClassDB::bind_method(D_METHOD("set_buffer_interpolated", "buffer_curr", "buffer_prev"), &MultiMesh::set_buffer_interpolated);
ADD_PROPERTY(PropertyInfo(Variant::INT, "transform_format", PROPERTY_HINT_ENUM, "2D,3D"), "set_transform_format", "get_transform_format");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_colors"), "set_use_colors", "is_using_colors");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_custom_data"), "set_use_custom_data", "is_using_custom_data");
@ -369,8 +417,14 @@ void MultiMesh::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::PACKED_COLOR_ARRAY, "custom_data_array", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "_set_custom_data_array", "_get_custom_data_array");
#endif
ADD_GROUP("Physics Interpolation", "physics_interpolation");
ADD_PROPERTY(PropertyInfo(Variant::INT, "physics_interpolation_quality", PROPERTY_HINT_ENUM, "Fast,High"), "set_physics_interpolation_quality", "get_physics_interpolation_quality");
BIND_ENUM_CONSTANT(TRANSFORM_2D);
BIND_ENUM_CONSTANT(TRANSFORM_3D);
BIND_ENUM_CONSTANT(INTERP_QUALITY_FAST);
BIND_ENUM_CONSTANT(INTERP_QUALITY_HIGH);
}
MultiMesh::MultiMesh() {

View file

@ -44,6 +44,11 @@ public:
TRANSFORM_3D = RS::MULTIMESH_TRANSFORM_3D
};
enum PhysicsInterpolationQuality {
INTERP_QUALITY_FAST,
INTERP_QUALITY_HIGH,
};
private:
Ref<Mesh> mesh;
RID multimesh;
@ -53,6 +58,7 @@ private:
bool use_custom_data = false;
int instance_count = 0;
int visible_instance_count = -1;
PhysicsInterpolationQuality _physics_interpolation_quality = INTERP_QUALITY_FAST;
protected:
static void _bind_methods();
@ -74,6 +80,8 @@ protected:
void set_buffer(const Vector<float> &p_buffer);
Vector<float> get_buffer() const;
void set_buffer_interpolated(const Vector<float> &p_buffer_curr, const Vector<float> &p_buffer_prev);
public:
void set_mesh(const Ref<Mesh> &p_mesh);
Ref<Mesh> get_mesh() const;
@ -93,6 +101,9 @@ public:
void set_visible_instance_count(int p_count);
int get_visible_instance_count() const;
void set_physics_interpolation_quality(PhysicsInterpolationQuality p_quality);
PhysicsInterpolationQuality get_physics_interpolation_quality() const { return _physics_interpolation_quality; }
void set_instance_transform(int p_instance, const Transform3D &p_transform);
void set_instance_transform_2d(int p_instance, const Transform2D &p_transform);
Transform3D get_instance_transform(int p_instance) const;
@ -104,6 +115,10 @@ public:
void set_instance_custom_data(int p_instance, const Color &p_custom_data);
Color get_instance_custom_data(int p_instance) const;
void reset_instance_physics_interpolation(int p_instance);
void set_physics_interpolated(bool p_interpolated);
void set_custom_aabb(const AABB &p_custom);
AABB get_custom_aabb() const;
@ -116,5 +131,6 @@ public:
};
VARIANT_ENUM_CAST(MultiMesh::TransformFormat);
VARIANT_ENUM_CAST(MultiMesh::PhysicsInterpolationQuality);
#endif // MULTIMESH_H

Some files were not shown because too many files have changed in this diff Show more