feat: updated engine version to 4.4-rc1
This commit is contained in:
parent
ee00efde1f
commit
21ba8e33af
5459 changed files with 1128836 additions and 198305 deletions
|
|
@ -1,4 +1,5 @@
|
|||
#!/usr/bin/env python
|
||||
from misc.utility.scons_hints import *
|
||||
|
||||
Import("env")
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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(); }
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
#!/usr/bin/env python
|
||||
from misc.utility.scons_hints import *
|
||||
|
||||
Import("env")
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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(); }
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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>();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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() {}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ private:
|
|||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
void _on_format_changed();
|
||||
|
||||
public:
|
||||
virtual int get_width() const override;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
46
engine/scene/resources/color_palette.cpp
Normal file
46
engine/scene/resources/color_palette.cpp
Normal 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");
|
||||
}
|
||||
50
engine/scene/resources/color_palette.h
Normal file
50
engine/scene/resources/color_palette.h
Normal 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
|
||||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -67,6 +67,7 @@ public:
|
|||
TONE_MAPPER_REINHARDT,
|
||||
TONE_MAPPER_FILMIC,
|
||||
TONE_MAPPER_ACES,
|
||||
TONE_MAPPER_AGX,
|
||||
};
|
||||
|
||||
enum SDFGIYScale {
|
||||
|
|
|
|||
110
engine/scene/resources/external_texture.cpp
Normal file
110
engine/scene/resources/external_texture.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
69
engine/scene/resources/external_texture.h
Normal file
69
engine/scene/resources/external_texture.h
Normal 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
|
||||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -312,6 +312,8 @@ void ImmediateMesh::surface_end() {
|
|||
uses_uv2s = false;
|
||||
|
||||
surface_active = false;
|
||||
|
||||
emit_changed();
|
||||
}
|
||||
|
||||
void ImmediateMesh::clear_surfaces() {
|
||||
|
|
|
|||
|
|
@ -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()) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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
Loading…
Add table
Add a link
Reference in a new issue