Move and organize tests into subfolders

This commit is contained in:
Aaron Franke
2021-11-06 16:05:33 -05:00
parent 9f46ce8652
commit 99a282f631
63 changed files with 90 additions and 184 deletions

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,41 @@
/*************************************************************************/
/* test_physics_2d.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef TEST_PHYSICS_2D_H
#define TEST_PHYSICS_2D_H
class MainLoop;
namespace TestPhysics2D {
MainLoop *test();
}
#endif // TEST_PHYSICS_2D_H

View File

@ -0,0 +1,410 @@
/*************************************************************************/
/* test_physics_3d.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "test_physics_3d.h"
#include "core/math/convex_hull.h"
#include "core/math/geometry_3d.h"
#include "core/os/main_loop.h"
#include "servers/physics_server_3d.h"
#include "servers/rendering_server.h"
class TestPhysics3DMainLoop : public MainLoop {
GDCLASS(TestPhysics3DMainLoop, MainLoop);
enum {
LINK_COUNT = 20,
};
RID test_cube;
RID plane;
RID sphere;
RID light;
RID camera;
RID mover;
RID scenario;
RID space;
RID character;
real_t ofs_x, ofs_y;
Point2 joy_direction;
List<RID> bodies;
Map<PhysicsServer3D::ShapeType, RID> type_shape_map;
Map<PhysicsServer3D::ShapeType, RID> type_mesh_map;
void body_changed_transform(Object *p_state, RID p_visual_instance) {
PhysicsDirectBodyState3D *state = (PhysicsDirectBodyState3D *)p_state;
RenderingServer *vs = RenderingServer::get_singleton();
Transform3D t = state->get_transform();
vs->instance_set_transform(p_visual_instance, t);
}
bool quit;
protected:
RID create_body(PhysicsServer3D::ShapeType p_shape, PhysicsServer3D::BodyMode p_body, const Transform3D p_location, bool p_active_default = true, const Transform3D &p_shape_xform = Transform3D()) {
RenderingServer *vs = RenderingServer::get_singleton();
PhysicsServer3D *ps = PhysicsServer3D::get_singleton();
RID mesh_instance = vs->instance_create2(type_mesh_map[p_shape], scenario);
RID body = ps->body_create();
ps->body_set_mode(body, p_body);
ps->body_set_state(body, PhysicsServer3D::BODY_STATE_SLEEPING, !p_active_default);
ps->body_set_space(body, space);
ps->body_set_param(body, PhysicsServer3D::BODY_PARAM_BOUNCE, 0.0);
//todo set space
ps->body_add_shape(body, type_shape_map[p_shape]);
ps->body_set_force_integration_callback(body, callable_mp(this, &TestPhysics3DMainLoop::body_changed_transform), mesh_instance);
ps->body_set_state(body, PhysicsServer3D::BODY_STATE_TRANSFORM, p_location);
bodies.push_back(body);
if (p_body == PhysicsServer3D::BODY_MODE_STATIC) {
vs->instance_set_transform(mesh_instance, p_location);
}
return body;
}
RID create_world_boundary(const Plane &p_plane) {
PhysicsServer3D *ps = PhysicsServer3D::get_singleton();
RID world_boundary_shape = ps->shape_create(PhysicsServer3D::SHAPE_WORLD_BOUNDARY);
ps->shape_set_data(world_boundary_shape, p_plane);
RID b = ps->body_create();
ps->body_set_mode(b, PhysicsServer3D::BODY_MODE_STATIC);
ps->body_set_space(b, space);
ps->body_add_shape(b, world_boundary_shape);
return b;
}
void configure_body(RID p_body, real_t p_mass, real_t p_friction, real_t p_bounce) {
PhysicsServer3D *ps = PhysicsServer3D::get_singleton();
ps->body_set_param(p_body, PhysicsServer3D::BODY_PARAM_MASS, p_mass);
ps->body_set_param(p_body, PhysicsServer3D::BODY_PARAM_FRICTION, p_friction);
ps->body_set_param(p_body, PhysicsServer3D::BODY_PARAM_BOUNCE, p_bounce);
}
void initialize_shapes() {
RenderingServer *vs = RenderingServer::get_singleton();
PhysicsServer3D *ps = PhysicsServer3D::get_singleton();
/* SPHERE SHAPE */
RID sphere_mesh = vs->make_sphere_mesh(10, 20, 0.5);
type_mesh_map[PhysicsServer3D::SHAPE_SPHERE] = sphere_mesh;
RID sphere_shape = ps->shape_create(PhysicsServer3D::SHAPE_SPHERE);
ps->shape_set_data(sphere_shape, 0.5);
type_shape_map[PhysicsServer3D::SHAPE_SPHERE] = sphere_shape;
/* BOX SHAPE */
Vector<Plane> box_planes = Geometry3D::build_box_planes(Vector3(0.5, 0.5, 0.5));
RID box_mesh = vs->mesh_create();
Geometry3D::MeshData box_data = Geometry3D::build_convex_mesh(box_planes);
vs->mesh_add_surface_from_mesh_data(box_mesh, box_data);
type_mesh_map[PhysicsServer3D::SHAPE_BOX] = box_mesh;
RID box_shape = ps->shape_create(PhysicsServer3D::SHAPE_BOX);
ps->shape_set_data(box_shape, Vector3(0.5, 0.5, 0.5));
type_shape_map[PhysicsServer3D::SHAPE_BOX] = box_shape;
/* CAPSULE SHAPE */
Vector<Plane> capsule_planes = Geometry3D::build_capsule_planes(0.5, 0.7, 12, Vector3::AXIS_Z);
RID capsule_mesh = vs->mesh_create();
Geometry3D::MeshData capsule_data = Geometry3D::build_convex_mesh(capsule_planes);
vs->mesh_add_surface_from_mesh_data(capsule_mesh, capsule_data);
type_mesh_map[PhysicsServer3D::SHAPE_CAPSULE] = capsule_mesh;
RID capsule_shape = ps->shape_create(PhysicsServer3D::SHAPE_CAPSULE);
Dictionary capsule_params;
capsule_params["radius"] = 0.5;
capsule_params["height"] = 1.4;
ps->shape_set_data(capsule_shape, capsule_params);
type_shape_map[PhysicsServer3D::SHAPE_CAPSULE] = capsule_shape;
/* CONVEX SHAPE */
Vector<Plane> convex_planes = Geometry3D::build_cylinder_planes(0.5, 0.7, 5, Vector3::AXIS_Z);
RID convex_mesh = vs->mesh_create();
Geometry3D::MeshData convex_data = Geometry3D::build_convex_mesh(convex_planes);
ConvexHullComputer::convex_hull(convex_data.vertices, convex_data);
vs->mesh_add_surface_from_mesh_data(convex_mesh, convex_data);
type_mesh_map[PhysicsServer3D::SHAPE_CONVEX_POLYGON] = convex_mesh;
RID convex_shape = ps->shape_create(PhysicsServer3D::SHAPE_CONVEX_POLYGON);
ps->shape_set_data(convex_shape, convex_data.vertices);
type_shape_map[PhysicsServer3D::SHAPE_CONVEX_POLYGON] = convex_shape;
}
void make_trimesh(Vector<Vector3> p_faces, const Transform3D &p_xform = Transform3D()) {
RenderingServer *vs = RenderingServer::get_singleton();
PhysicsServer3D *ps = PhysicsServer3D::get_singleton();
RID trimesh_shape = ps->shape_create(PhysicsServer3D::SHAPE_CONCAVE_POLYGON);
Dictionary trimesh_params;
trimesh_params["faces"] = p_faces;
trimesh_params["backface_collision"] = false;
ps->shape_set_data(trimesh_shape, trimesh_params);
Vector<Vector3> normals; // for drawing
for (int i = 0; i < p_faces.size() / 3; i++) {
Plane p(p_faces[i * 3 + 0], p_faces[i * 3 + 1], p_faces[i * 3 + 2]);
normals.push_back(p.normal);
normals.push_back(p.normal);
normals.push_back(p.normal);
}
RID trimesh_mesh = vs->mesh_create();
Array d;
d.resize(RS::ARRAY_MAX);
d[RS::ARRAY_VERTEX] = p_faces;
d[RS::ARRAY_NORMAL] = normals;
vs->mesh_add_surface_from_arrays(trimesh_mesh, RS::PRIMITIVE_TRIANGLES, d);
RID triins = vs->instance_create2(trimesh_mesh, scenario);
RID tribody = ps->body_create();
ps->body_set_mode(tribody, PhysicsServer3D::BODY_MODE_STATIC);
ps->body_set_space(tribody, space);
//todo set space
ps->body_add_shape(tribody, trimesh_shape);
Transform3D tritrans = p_xform;
ps->body_set_state(tribody, PhysicsServer3D::BODY_STATE_TRANSFORM, tritrans);
vs->instance_set_transform(triins, tritrans);
}
void make_grid(int p_width, int p_height, real_t p_cellsize, real_t p_cellheight, const Transform3D &p_xform = Transform3D()) {
Vector<Vector<real_t>> grid;
grid.resize(p_width);
for (int i = 0; i < p_width; i++) {
grid.write[i].resize(p_height);
for (int j = 0; j < p_height; j++) {
grid.write[i].write[j] = 1.0 + Math::random(-p_cellheight, p_cellheight);
}
}
Vector<Vector3> faces;
for (int i = 1; i < p_width; i++) {
for (int j = 1; j < p_height; j++) {
#define MAKE_VERTEX(m_x, m_z) \
faces.push_back(Vector3((m_x - p_width / 2) * p_cellsize, grid[m_x][m_z], (m_z - p_height / 2) * p_cellsize))
MAKE_VERTEX(i, j - 1);
MAKE_VERTEX(i, j);
MAKE_VERTEX(i - 1, j);
MAKE_VERTEX(i - 1, j - 1);
MAKE_VERTEX(i, j - 1);
MAKE_VERTEX(i - 1, j);
}
}
make_trimesh(faces, p_xform);
}
public:
virtual void input_event(const Ref<InputEvent> &p_event) {
Ref<InputEventMouseMotion> mm = p_event;
if (mm.is_valid() && mm->get_button_mask() & 4) {
ofs_y -= mm->get_relative().y / 200.0;
ofs_x += mm->get_relative().x / 200.0;
}
if (mm.is_valid() && mm->get_button_mask() & 1) {
real_t y = -mm->get_relative().y / 20.0;
real_t x = mm->get_relative().x / 20.0;
if (mover.is_valid()) {
PhysicsServer3D *ps = PhysicsServer3D::get_singleton();
Transform3D t = ps->body_get_state(mover, PhysicsServer3D::BODY_STATE_TRANSFORM);
t.origin += Vector3(x, y, 0);
ps->body_set_state(mover, PhysicsServer3D::BODY_STATE_TRANSFORM, t);
}
}
}
virtual void request_quit() {
quit = true;
}
virtual void initialize() override {
ofs_x = ofs_y = 0;
initialize_shapes();
PhysicsServer3D *ps = PhysicsServer3D::get_singleton();
space = ps->space_create();
ps->space_set_active(space, true);
RenderingServer *vs = RenderingServer::get_singleton();
/* LIGHT */
RID lightaux = vs->directional_light_create();
scenario = vs->scenario_create();
vs->light_set_shadow(lightaux, true);
light = vs->instance_create2(lightaux, scenario);
Transform3D t;
t.rotate(Vector3(1.0, 0, 0), 0.6);
vs->instance_set_transform(light, t);
/* CAMERA */
camera = vs->camera_create();
RID viewport = vs->viewport_create();
Size2i screen_size = DisplayServer::get_singleton()->window_get_size();
vs->viewport_set_size(viewport, screen_size.x, screen_size.y);
vs->viewport_attach_to_screen(viewport, Rect2(Vector2(), screen_size));
vs->viewport_set_active(viewport, true);
vs->viewport_attach_camera(viewport, camera);
vs->viewport_set_scenario(viewport, scenario);
vs->camera_set_perspective(camera, 60, 0.1, 40.0);
vs->camera_set_transform(camera, Transform3D(Basis(), Vector3(0, 9, 12)));
Transform3D gxf;
gxf.basis.scale(Vector3(1.4, 0.4, 1.4));
gxf.origin = Vector3(-2, 1, -2);
make_grid(5, 5, 2.5, 1, gxf);
test_fall();
quit = false;
}
virtual bool physics_process(double p_time) override {
if (mover.is_valid()) {
static real_t joy_speed = 10;
PhysicsServer3D *ps = PhysicsServer3D::get_singleton();
Transform3D t = ps->body_get_state(mover, PhysicsServer3D::BODY_STATE_TRANSFORM);
t.origin += Vector3(joy_speed * joy_direction.x * p_time, -joy_speed * joy_direction.y * p_time, 0);
ps->body_set_state(mover, PhysicsServer3D::BODY_STATE_TRANSFORM, t);
};
Transform3D cameratr;
cameratr.rotate(Vector3(0, 1, 0), ofs_x);
cameratr.rotate(Vector3(1, 0, 0), -ofs_y);
cameratr.translate(Vector3(0, 2, 8));
RenderingServer *vs = RenderingServer::get_singleton();
vs->camera_set_transform(camera, cameratr);
return quit;
}
virtual void finalize() override {
}
void test_joint() {
}
void test_hinge() {
}
void test_character() {
RenderingServer *vs = RenderingServer::get_singleton();
PhysicsServer3D *ps = PhysicsServer3D::get_singleton();
Vector<Plane> capsule_planes = Geometry3D::build_capsule_planes(0.5, 1, 12, 5, Vector3::AXIS_Y);
RID capsule_mesh = vs->mesh_create();
Geometry3D::MeshData capsule_data = Geometry3D::build_convex_mesh(capsule_planes);
vs->mesh_add_surface_from_mesh_data(capsule_mesh, capsule_data);
type_mesh_map[PhysicsServer3D::SHAPE_CAPSULE] = capsule_mesh;
RID capsule_shape = ps->shape_create(PhysicsServer3D::SHAPE_CAPSULE);
Dictionary capsule_params;
capsule_params["radius"] = 0.5;
capsule_params["height"] = 1;
Transform3D shape_xform;
shape_xform.rotate(Vector3(1, 0, 0), Math_PI / 2.0);
//shape_xform.origin=Vector3(1,1,1);
ps->shape_set_data(capsule_shape, capsule_params);
RID mesh_instance = vs->instance_create2(capsule_mesh, scenario);
character = ps->body_create();
ps->body_set_mode(character, PhysicsServer3D::BODY_MODE_DYNAMIC_LINEAR);
ps->body_set_space(character, space);
//todo add space
ps->body_add_shape(character, capsule_shape);
ps->body_set_force_integration_callback(character, callable_mp(this, &TestPhysics3DMainLoop::body_changed_transform), mesh_instance);
ps->body_set_state(character, PhysicsServer3D::BODY_STATE_TRANSFORM, Transform3D(Basis(), Vector3(-2, 5, -2)));
bodies.push_back(character);
}
void test_fall() {
for (int i = 0; i < 35; i++) {
static const PhysicsServer3D::ShapeType shape_idx[] = {
PhysicsServer3D::SHAPE_CAPSULE,
PhysicsServer3D::SHAPE_BOX,
PhysicsServer3D::SHAPE_SPHERE,
PhysicsServer3D::SHAPE_CONVEX_POLYGON
};
PhysicsServer3D::ShapeType type = shape_idx[i % 4];
Transform3D t;
t.origin = Vector3(0.0 * i, 3.5 + 1.1 * i, 0.7 + 0.0 * i);
t.basis.rotate(Vector3(0.2, -1, 0), Math_PI / 2 * 0.6);
create_body(type, PhysicsServer3D::BODY_MODE_DYNAMIC, t);
}
create_world_boundary(Plane(Vector3(0, 1, 0), -1));
}
void test_activate() {
create_body(PhysicsServer3D::SHAPE_BOX, PhysicsServer3D::BODY_MODE_DYNAMIC, Transform3D(Basis(), Vector3(0, 2, 0)), true);
create_world_boundary(Plane(Vector3(0, 1, 0), -1));
}
virtual bool process(double p_time) override {
return false;
}
TestPhysics3DMainLoop() {
}
};
namespace TestPhysics3D {
MainLoop *test() {
return memnew(TestPhysics3DMainLoop);
}
} // namespace TestPhysics3D

View File

@ -0,0 +1,41 @@
/*************************************************************************/
/* test_physics_3d.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef TEST_PHYSICS_H
#define TEST_PHYSICS_H
class MainLoop;
namespace TestPhysics3D {
MainLoop *test();
}
#endif

View File

@ -0,0 +1,232 @@
/*************************************************************************/
/* test_render.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "test_render.h"
#include "core/math/convex_hull.h"
#include "core/os/main_loop.h"
#include "servers/rendering_server.h"
#define OBJECT_COUNT 50
namespace TestRender {
class TestMainLoop : public MainLoop {
RID test_cube;
RID instance;
RID camera;
RID viewport;
RID light;
RID scenario;
struct InstanceInfo {
RID instance;
Transform3D base;
Vector3 rot_axis;
};
List<InstanceInfo> instances;
float ofs;
bool quit;
protected:
public:
virtual void input_event(const Ref<InputEvent> &p_event) {
if (p_event->is_pressed()) {
quit = true;
}
}
virtual void init() {
print_line("INITIALIZING TEST RENDER");
RenderingServer *vs = RenderingServer::get_singleton();
test_cube = vs->get_test_cube();
scenario = vs->scenario_create();
Vector<Vector3> vts;
/*
Vector<Plane> sp = Geometry3D::build_sphere_planes(2,5,5);
Geometry3D::MeshData md2 = Geometry3D::build_convex_mesh(sp);
vts=md2.vertices;
*/
/*
static const int s = 20;
for(int i=0;i<s;i++) {
Basis rot(Vector3(0,1,0),i*Math_PI/s);
for(int j=0;j<s;j++) {
Vector3 v;
v.x=Math::sin(j*Math_PI*2/s);
v.y=Math::cos(j*Math_PI*2/s);
vts.push_back( rot.xform(v*2 ) );
}
}*/
/*for(int i=0;i<100;i++) {
vts.push_back( Vector3(Math::randf()*2-1.0,Math::randf()*2-1.0,Math::randf()*2-1.0).normalized()*2);
}*/
/*
vts.push_back(Vector3(0,0,1));
vts.push_back(Vector3(0,0,-1));
vts.push_back(Vector3(0,1,0));
vts.push_back(Vector3(0,-1,0));
vts.push_back(Vector3(1,0,0));
vts.push_back(Vector3(-1,0,0));*/
vts.push_back(Vector3(1, 1, 1));
vts.push_back(Vector3(1, -1, 1));
vts.push_back(Vector3(-1, 1, 1));
vts.push_back(Vector3(-1, -1, 1));
vts.push_back(Vector3(1, 1, -1));
vts.push_back(Vector3(1, -1, -1));
vts.push_back(Vector3(-1, 1, -1));
vts.push_back(Vector3(-1, -1, -1));
Geometry3D::MeshData md;
Error err = ConvexHullComputer::convex_hull(vts, md);
print_line("ERR: " + itos(err));
test_cube = vs->mesh_create();
vs->mesh_add_surface_from_mesh_data(test_cube, md);
//vs->scenario_set_debug(scenario,RS::SCENARIO_DEBUG_WIREFRAME);
/*
RID sm = vs->shader_create();
//vs->shader_set_fragment_code(sm,"OUT_ALPHA=mod(TIME,1);");
//vs->shader_set_vertex_code(sm,"OUT_VERTEX=IN_VERTEX*mod(TIME,1);");
vs->shader_set_fragment_code(sm,"OUT_DIFFUSE=vec3(1,0,1);OUT_GLOW=abs(sin(TIME));");
RID tcmat = vs->mesh_surface_get_material(test_cube,0);
vs->material_set_shader(tcmat,sm);
*/
List<String> cmdline = OS::get_singleton()->get_cmdline_args();
int object_count = OBJECT_COUNT;
if (cmdline.size() > 0 && cmdline[cmdline.size() - 1].to_int()) {
object_count = cmdline[cmdline.size() - 1].to_int();
};
for (int i = 0; i < object_count; i++) {
InstanceInfo ii;
ii.instance = vs->instance_create2(test_cube, scenario);
ii.base.translate(Math::random(-20, 20), Math::random(-20, 20), Math::random(-20, 18));
ii.base.rotate(Vector3(0, 1, 0), Math::randf() * Math_PI);
ii.base.rotate(Vector3(1, 0, 0), Math::randf() * Math_PI);
vs->instance_set_transform(ii.instance, ii.base);
ii.rot_axis = Vector3(Math::random(-1, 1), Math::random(-1, 1), Math::random(-1, 1)).normalized();
instances.push_back(ii);
}
camera = vs->camera_create();
// vs->camera_set_perspective( camera, 60.0,0.1, 100.0 );
viewport = vs->viewport_create();
Size2i screen_size = DisplayServer::get_singleton()->window_get_size();
vs->viewport_set_size(viewport, screen_size.x, screen_size.y);
vs->viewport_attach_to_screen(viewport, Rect2(Vector2(), screen_size));
vs->viewport_set_active(viewport, true);
vs->viewport_attach_camera(viewport, camera);
vs->viewport_set_scenario(viewport, scenario);
vs->camera_set_transform(camera, Transform3D(Basis(), Vector3(0, 3, 30)));
vs->camera_set_perspective(camera, 60, 0.1, 1000);
/*
RID lightaux = vs->light_create( RenderingServer::LIGHT_OMNI );
vs->light_set_var( lightaux, RenderingServer::LIGHT_VAR_RADIUS, 80 );
vs->light_set_var( lightaux, RenderingServer::LIGHT_VAR_ATTENUATION, 1 );
vs->light_set_var( lightaux, RenderingServer::LIGHT_VAR_ENERGY, 1.5 );
light = vs->instance_create( lightaux );
*/
RID lightaux;
lightaux = vs->directional_light_create();
//vs->light_set_color( lightaux, RenderingServer::LIGHT_COLOR_AMBIENT, Color(0.0,0.0,0.0) );
vs->light_set_color(lightaux, Color(1.0, 1.0, 1.0));
//vs->light_set_shadow( lightaux, true );
light = vs->instance_create2(lightaux, scenario);
Transform3D lla;
//lla.set_look_at(Vector3(),Vector3(1, -1, 1));
lla.basis = Basis::looking_at(Vector3(0.0, -0.836026, -0.548690));
vs->instance_set_transform(light, lla);
lightaux = vs->omni_light_create();
//vs->light_set_color( lightaux, RenderingServer::LIGHT_COLOR_AMBIENT, Color(0.0,0.0,1.0) );
vs->light_set_color(lightaux, Color(1.0, 1.0, 0.0));
vs->light_set_param(lightaux, RenderingServer::LIGHT_PARAM_RANGE, 4);
vs->light_set_param(lightaux, RenderingServer::LIGHT_PARAM_ENERGY, 8);
//vs->light_set_shadow( lightaux, true );
//light = vs->instance_create( lightaux );
ofs = 0;
quit = false;
}
virtual bool iteration(double p_time) {
RenderingServer *vs = RenderingServer::get_singleton();
//Transform3D t;
//t.rotate(Vector3(0, 1, 0), ofs);
//t.translate(Vector3(0,0,20 ));
//vs->camera_set_transform(camera, t);
ofs += p_time * 0.05;
//return quit;
for (const InstanceInfo &E : instances) {
Transform3D pre(Basis(E.rot_axis, ofs), Vector3());
vs->instance_set_transform(E.instance, pre * E.base);
/*
if( !E->next() ) {
vs->free( E.instance );
instances.erase(E );
}*/
}
return quit;
}
virtual bool idle(double p_time) {
return quit;
}
virtual void finish() {
}
};
MainLoop *test() {
return memnew(TestMainLoop);
}
} // namespace TestRender

View File

@ -0,0 +1,41 @@
/*************************************************************************/
/* test_render.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef TEST_RENDER_H
#define TEST_RENDER_H
class MainLoop;
namespace TestRender {
MainLoop *test();
}
#endif // TEST_RENDER_H

View File

@ -0,0 +1,360 @@
/*************************************************************************/
/* test_shader_lang.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "test_shader_lang.h"
#include "core/os/main_loop.h"
#include "core/os/os.h"
#include "servers/rendering/shader_language.h"
typedef ShaderLanguage SL;
namespace TestShaderLang {
static String _mktab(int p_level) {
String tb;
for (int i = 0; i < p_level; i++) {
tb += "\t";
}
return tb;
}
static String _typestr(SL::DataType p_type) {
return ShaderLanguage::get_datatype_name(p_type);
}
static String _prestr(SL::DataPrecision p_pres) {
switch (p_pres) {
case SL::PRECISION_LOWP:
return "lowp ";
case SL::PRECISION_MEDIUMP:
return "mediump ";
case SL::PRECISION_HIGHP:
return "highp ";
case SL::PRECISION_DEFAULT:
return "";
}
return "";
}
static String _opstr(SL::Operator p_op) {
return ShaderLanguage::get_operator_text(p_op);
}
static String get_constant_text(SL::DataType p_type, const Vector<SL::ConstantNode::Value> &p_values) {
switch (p_type) {
case SL::TYPE_BOOL:
return p_values[0].boolean ? "true" : "false";
case SL::TYPE_BVEC2:
return String() + "bvec2(" + (p_values[0].boolean ? "true" : "false") + (p_values[1].boolean ? "true" : "false") + ")";
case SL::TYPE_BVEC3:
return String() + "bvec3(" + (p_values[0].boolean ? "true" : "false") + "," + (p_values[1].boolean ? "true" : "false") + "," + (p_values[2].boolean ? "true" : "false") + ")";
case SL::TYPE_BVEC4:
return String() + "bvec4(" + (p_values[0].boolean ? "true" : "false") + "," + (p_values[1].boolean ? "true" : "false") + "," + (p_values[2].boolean ? "true" : "false") + "," + (p_values[3].boolean ? "true" : "false") + ")";
case SL::TYPE_INT:
return rtos(p_values[0].sint);
case SL::TYPE_IVEC2:
return String() + "ivec2(" + rtos(p_values[0].sint) + "," + rtos(p_values[1].sint) + ")";
case SL::TYPE_IVEC3:
return String() + "ivec3(" + rtos(p_values[0].sint) + "," + rtos(p_values[1].sint) + "," + rtos(p_values[2].sint) + ")";
case SL::TYPE_IVEC4:
return String() + "ivec4(" + rtos(p_values[0].sint) + "," + rtos(p_values[1].sint) + "," + rtos(p_values[2].sint) + "," + rtos(p_values[3].sint) + ")";
case SL::TYPE_UINT:
return rtos(p_values[0].real);
case SL::TYPE_UVEC2:
return String() + "uvec2(" + rtos(p_values[0].real) + "," + rtos(p_values[1].real) + ")";
case SL::TYPE_UVEC3:
return String() + "uvec3(" + rtos(p_values[0].real) + "," + rtos(p_values[1].real) + "," + rtos(p_values[2].real) + ")";
case SL::TYPE_UVEC4:
return String() + "uvec4(" + rtos(p_values[0].real) + "," + rtos(p_values[1].real) + "," + rtos(p_values[2].real) + "," + rtos(p_values[3].real) + ")";
case SL::TYPE_FLOAT:
return rtos(p_values[0].real);
case SL::TYPE_VEC2:
return String() + "vec2(" + rtos(p_values[0].real) + "," + rtos(p_values[1].real) + ")";
case SL::TYPE_VEC3:
return String() + "vec3(" + rtos(p_values[0].real) + "," + rtos(p_values[1].real) + "," + rtos(p_values[2].real) + ")";
case SL::TYPE_VEC4:
return String() + "vec4(" + rtos(p_values[0].real) + "," + rtos(p_values[1].real) + "," + rtos(p_values[2].real) + "," + rtos(p_values[3].real) + ")";
default:
ERR_FAIL_V(String());
}
}
static String dump_node_code(SL::Node *p_node, int p_level) {
String code;
switch (p_node->type) {
case SL::Node::TYPE_SHADER: {
SL::ShaderNode *pnode = (SL::ShaderNode *)p_node;
for (const KeyValue<StringName, SL::ShaderNode::Uniform> &E : pnode->uniforms) {
String ucode = "uniform ";
ucode += _prestr(E.value.precision);
ucode += _typestr(E.value.type);
ucode += " " + String(E.key);
if (E.value.array_size > 0) {
ucode += "[";
ucode += itos(E.value.array_size);
ucode += "]";
} else {
if (E.value.default_value.size()) {
ucode += " = " + get_constant_text(E.value.type, E.value.default_value);
}
static const char *hint_name[SL::ShaderNode::Uniform::HINT_MAX] = {
"",
"color",
"range",
"albedo",
"normal",
"black",
"white"
};
if (E.value.hint) {
ucode += " : " + String(hint_name[E.value.hint]);
}
}
code += ucode + "\n";
}
for (const KeyValue<StringName, SL::ShaderNode::Varying> &E : pnode->varyings) {
String vcode = "varying ";
vcode += _prestr(E.value.precision);
vcode += _typestr(E.value.type);
vcode += " " + String(E.key);
code += vcode + "\n";
}
for (int i = 0; i < pnode->functions.size(); i++) {
SL::FunctionNode *fnode = pnode->functions[i].function;
String header;
header = _typestr(fnode->return_type) + " " + fnode->name + "(";
for (int j = 0; j < fnode->arguments.size(); j++) {
if (j > 0) {
header += ", ";
}
header += _prestr(fnode->arguments[j].precision) + _typestr(fnode->arguments[j].type) + " " + fnode->arguments[j].name;
}
header += ")\n";
code += header;
code += dump_node_code(fnode->body, p_level + 1);
}
//code+=dump_node_code(pnode->body,p_level);
} break;
case SL::Node::TYPE_STRUCT: {
} break;
case SL::Node::TYPE_FUNCTION: {
} break;
case SL::Node::TYPE_BLOCK: {
SL::BlockNode *bnode = (SL::BlockNode *)p_node;
//variables
code += _mktab(p_level - 1) + "{\n";
for (const KeyValue<StringName, SL::BlockNode::Variable> &E : bnode->variables) {
code += _mktab(p_level) + _prestr(E.value.precision) + _typestr(E.value.type) + " " + E.key + ";\n";
}
for (int i = 0; i < bnode->statements.size(); i++) {
String scode = dump_node_code(bnode->statements[i], p_level);
if (bnode->statements[i]->type == SL::Node::TYPE_CONTROL_FLOW) {
code += scode; //use directly
} else {
code += _mktab(p_level) + scode + ";\n";
}
}
code += _mktab(p_level - 1) + "}\n";
} break;
case SL::Node::TYPE_VARIABLE: {
SL::VariableNode *vnode = (SL::VariableNode *)p_node;
code = vnode->name;
} break;
case SL::Node::TYPE_VARIABLE_DECLARATION: {
// FIXME: Implement
} break;
case SL::Node::TYPE_ARRAY: {
SL::ArrayNode *vnode = (SL::ArrayNode *)p_node;
code = vnode->name;
} break;
case SL::Node::TYPE_ARRAY_DECLARATION: {
// FIXME: Implement
} break;
case SL::Node::TYPE_ARRAY_CONSTRUCT: {
// FIXME: Implement
} break;
case SL::Node::TYPE_CONSTANT: {
SL::ConstantNode *cnode = (SL::ConstantNode *)p_node;
return get_constant_text(cnode->datatype, cnode->values);
} break;
case SL::Node::TYPE_OPERATOR: {
SL::OperatorNode *onode = (SL::OperatorNode *)p_node;
switch (onode->op) {
case SL::OP_ASSIGN:
case SL::OP_ASSIGN_ADD:
case SL::OP_ASSIGN_SUB:
case SL::OP_ASSIGN_MUL:
case SL::OP_ASSIGN_DIV:
case SL::OP_ASSIGN_SHIFT_LEFT:
case SL::OP_ASSIGN_SHIFT_RIGHT:
case SL::OP_ASSIGN_MOD:
case SL::OP_ASSIGN_BIT_AND:
case SL::OP_ASSIGN_BIT_OR:
case SL::OP_ASSIGN_BIT_XOR:
code = dump_node_code(onode->arguments[0], p_level) + _opstr(onode->op) + dump_node_code(onode->arguments[1], p_level);
break;
case SL::OP_BIT_INVERT:
case SL::OP_NEGATE:
case SL::OP_NOT:
case SL::OP_DECREMENT:
case SL::OP_INCREMENT:
code = _opstr(onode->op) + dump_node_code(onode->arguments[0], p_level);
break;
case SL::OP_POST_DECREMENT:
case SL::OP_POST_INCREMENT:
code = dump_node_code(onode->arguments[0], p_level) + _opstr(onode->op);
break;
case SL::OP_CALL:
case SL::OP_CONSTRUCT:
code = dump_node_code(onode->arguments[0], p_level) + "(";
for (int i = 1; i < onode->arguments.size(); i++) {
if (i > 1) {
code += ", ";
}
code += dump_node_code(onode->arguments[i], p_level);
}
code += ")";
break;
default: {
code = "(" + dump_node_code(onode->arguments[0], p_level) + _opstr(onode->op) + dump_node_code(onode->arguments[1], p_level) + ")";
break;
}
}
} break;
case SL::Node::TYPE_CONTROL_FLOW: {
SL::ControlFlowNode *cfnode = (SL::ControlFlowNode *)p_node;
if (cfnode->flow_op == SL::FLOW_OP_IF) {
code += _mktab(p_level) + "if (" + dump_node_code(cfnode->expressions[0], p_level) + ")\n";
code += dump_node_code(cfnode->blocks[0], p_level + 1);
if (cfnode->blocks.size() == 2) {
code += _mktab(p_level) + "else\n";
code += dump_node_code(cfnode->blocks[1], p_level + 1);
}
} else if (cfnode->flow_op == SL::FLOW_OP_RETURN) {
if (cfnode->blocks.size()) {
code = "return " + dump_node_code(cfnode->blocks[0], p_level);
} else {
code = "return";
}
}
} break;
case SL::Node::TYPE_MEMBER: {
SL::MemberNode *mnode = (SL::MemberNode *)p_node;
code = dump_node_code(mnode->owner, p_level) + "." + mnode->name;
} break;
}
return code;
}
static Error recreate_code(void *p_str, SL::ShaderNode *p_program) {
String *str = (String *)p_str;
*str = dump_node_code(p_program, 0);
return OK;
}
MainLoop *test() {
List<String> cmdlargs = OS::get_singleton()->get_cmdline_args();
if (cmdlargs.is_empty()) {
//try editor!
print_line("usage: godot -test shader_lang <shader>");
return nullptr;
}
String test = cmdlargs.back()->get();
FileAccess *fa = FileAccess::open(test, FileAccess::READ);
if (!fa) {
ERR_FAIL_V(nullptr);
}
String code;
while (true) {
char32_t c = fa->get_8();
if (fa->eof_reached()) {
break;
}
code += c;
}
SL sl;
print_line("tokens:\n\n" + sl.token_debug(code));
Map<StringName, SL::FunctionInfo> dt;
dt["fragment"].built_ins["ALBEDO"] = SL::TYPE_VEC3;
dt["fragment"].can_discard = true;
Vector<StringName> rm;
rm.push_back("popo");
Set<String> types;
types.insert("spatial");
Error err = sl.compile(code, dt, rm, ShaderLanguage::VaryingFunctionNames(), types, nullptr);
if (err) {
print_line("Error at line: " + rtos(sl.get_error_line()) + ": " + sl.get_error_text());
return nullptr;
} else {
String code2;
recreate_code(&code2, sl.get_shader());
print_line("code:\n\n" + code2);
}
return nullptr;
}
} // namespace TestShaderLang

View File

@ -0,0 +1,41 @@
/*************************************************************************/
/* test_shader_lang.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef TEST_SHADER_LANG_H
#define TEST_SHADER_LANG_H
class MainLoop;
namespace TestShaderLang {
MainLoop *test();
}
#endif // TEST_SHADER_LANG_H

View File

@ -0,0 +1,296 @@
/*************************************************************************/
/* test_text_server.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifdef TOOLS_ENABLED
#ifndef TEST_TEXT_SERVER_H
#define TEST_TEXT_SERVER_H
#include "editor/builtin_fonts.gen.h"
#include "servers/text_server.h"
#include "tests/test_macros.h"
namespace TestTextServer {
TEST_SUITE("[[TextServer]") {
TEST_CASE("[TextServer] Init, font loading and shaping") {
SUBCASE("[TextServer] Loading fonts") {
for (int i = 0; i < TextServerManager::get_singleton()->get_interface_count(); i++) {
Ref<TextServer> ts = TextServerManager::get_singleton()->get_interface(i);
TEST_FAIL_COND(ts.is_null(), "Invalid TS interface.");
RID font = ts->create_font();
ts->font_set_data_ptr(font, _font_NotoSans_Regular, _font_NotoSans_Regular_size);
TEST_FAIL_COND(font == RID(), "Loading font failed.");
ts->free(font);
}
}
SUBCASE("[TextServer] Text layout: Font fallback") {
for (int i = 0; i < TextServerManager::get_singleton()->get_interface_count(); i++) {
Ref<TextServer> ts = TextServerManager::get_singleton()->get_interface(i);
TEST_FAIL_COND(ts.is_null(), "Invalid TS interface.");
RID font1 = ts->create_font();
ts->font_set_data_ptr(font1, _font_NotoSans_Regular, _font_NotoSans_Regular_size);
RID font2 = ts->create_font();
ts->font_set_data_ptr(font2, _font_NotoSansThaiUI_Regular, _font_NotoSansThaiUI_Regular_size);
Vector<RID> font;
font.push_back(font1);
font.push_back(font2);
String test = U"คนอ้วน khon uan ראה";
// 6^ 17^
RID ctx = ts->create_shaped_text();
TEST_FAIL_COND(ctx == RID(), "Creating text buffer failed.");
bool ok = ts->shaped_text_add_string(ctx, test, font, 16);
TEST_FAIL_COND(!ok, "Adding text to the buffer failed.");
const Glyph *glyphs = ts->shaped_text_get_glyphs(ctx);
int gl_size = ts->shaped_text_get_glyph_count(ctx);
TEST_FAIL_COND(gl_size == 0, "Shaping failed");
for (int j = 0; j < gl_size; j++) {
if (glyphs[j].start < 6) {
TEST_FAIL_COND(glyphs[j].font_rid != font[1], "Incorrect font selected.");
}
if ((glyphs[j].start > 6) && (glyphs[j].start < 16)) {
TEST_FAIL_COND(glyphs[j].font_rid != font[0], "Incorrect font selected.");
}
if (glyphs[j].start > 16) {
TEST_FAIL_COND(glyphs[j].font_rid != RID(), "Incorrect font selected.");
TEST_FAIL_COND(glyphs[j].index != test[glyphs[j].start], "Incorrect glyph index.");
}
TEST_FAIL_COND((glyphs[j].start < 0 || glyphs[j].end > test.length()), "Incorrect glyph range.");
TEST_FAIL_COND(glyphs[j].font_size != 16, "Incorrect glyph font size.");
}
ts->free(ctx);
for (int j = 0; j < font.size(); j++) {
ts->free(font[j]);
}
font.clear();
}
}
SUBCASE("[TextServer] Text layout: BiDi") {
for (int i = 0; i < TextServerManager::get_singleton()->get_interface_count(); i++) {
Ref<TextServer> ts = TextServerManager::get_singleton()->get_interface(i);
TEST_FAIL_COND(ts.is_null(), "Invalid TS interface.");
if (!ts->has_feature(TextServer::FEATURE_BIDI_LAYOUT)) {
continue;
}
RID font1 = ts->create_font();
ts->font_set_data_ptr(font1, _font_NotoSans_Regular, _font_NotoSans_Regular_size);
RID font2 = ts->create_font();
ts->font_set_data_ptr(font2, _font_NotoNaskhArabicUI_Regular, _font_NotoNaskhArabicUI_Regular_size);
Vector<RID> font;
font.push_back(font1);
font.push_back(font2);
String test = U"Arabic (اَلْعَرَبِيَّةُ, al-ʿarabiyyah)";
// 7^ 26^
RID ctx = ts->create_shaped_text();
TEST_FAIL_COND(ctx == RID(), "Creating text buffer failed.");
bool ok = ts->shaped_text_add_string(ctx, test, font, 16);
TEST_FAIL_COND(!ok, "Adding text to the buffer failed.");
const Glyph *glyphs = ts->shaped_text_get_glyphs(ctx);
int gl_size = ts->shaped_text_get_glyph_count(ctx);
TEST_FAIL_COND(gl_size == 0, "Shaping failed");
for (int j = 0; j < gl_size; j++) {
if (glyphs[j].count > 0) {
if (glyphs[j].start < 7) {
TEST_FAIL_COND(((glyphs[j].flags & TextServer::GRAPHEME_IS_RTL) == TextServer::GRAPHEME_IS_RTL), "Incorrect direction.");
}
if ((glyphs[j].start > 8) && (glyphs[j].start < 23)) {
TEST_FAIL_COND(((glyphs[j].flags & TextServer::GRAPHEME_IS_RTL) != TextServer::GRAPHEME_IS_RTL), "Incorrect direction.");
}
if (glyphs[j].start > 26) {
TEST_FAIL_COND(((glyphs[j].flags & TextServer::GRAPHEME_IS_RTL) == TextServer::GRAPHEME_IS_RTL), "Incorrect direction.");
}
}
}
ts->free(ctx);
for (int j = 0; j < font.size(); j++) {
ts->free(font[j]);
}
font.clear();
}
}
SUBCASE("[TextServer] Text layout: Line breaking") {
for (int i = 0; i < TextServerManager::get_singleton()->get_interface_count(); i++) {
Ref<TextServer> ts = TextServerManager::get_singleton()->get_interface(i);
TEST_FAIL_COND(ts.is_null(), "Invalid TS interface.");
String test_1 = U"test test test";
// 5^ 10^
RID font1 = ts->create_font();
ts->font_set_data_ptr(font1, _font_NotoSans_Regular, _font_NotoSans_Regular_size);
RID font2 = ts->create_font();
ts->font_set_data_ptr(font2, _font_NotoSansThaiUI_Regular, _font_NotoSansThaiUI_Regular_size);
Vector<RID> font;
font.push_back(font1);
font.push_back(font2);
RID ctx = ts->create_shaped_text();
TEST_FAIL_COND(ctx == RID(), "Creating text buffer failed.");
bool ok = ts->shaped_text_add_string(ctx, test_1, font, 16);
TEST_FAIL_COND(!ok, "Adding text to the buffer failed.");
PackedInt32Array brks = ts->shaped_text_get_line_breaks(ctx, 1);
TEST_FAIL_COND(brks.size() != 6, "Invalid line breaks number.");
if (brks.size() == 6) {
TEST_FAIL_COND(brks[0] != 0, "Invalid line break position.");
TEST_FAIL_COND(brks[1] != 5, "Invalid line break position.");
TEST_FAIL_COND(brks[2] != 5, "Invalid line break position.");
TEST_FAIL_COND(brks[3] != 10, "Invalid line break position.");
TEST_FAIL_COND(brks[4] != 10, "Invalid line break position.");
TEST_FAIL_COND(brks[5] != 14, "Invalid line break position.");
}
ts->free(ctx);
for (int j = 0; j < font.size(); j++) {
ts->free(font[j]);
}
font.clear();
}
}
SUBCASE("[TextServer] Text layout: Justification") {
for (int i = 0; i < TextServerManager::get_singleton()->get_interface_count(); i++) {
Ref<TextServer> ts = TextServerManager::get_singleton()->get_interface(i);
TEST_FAIL_COND(ts.is_null(), "Invalid TS interface.");
RID font1 = ts->create_font();
ts->font_set_data_ptr(font1, _font_NotoSans_Regular, _font_NotoSans_Regular_size);
RID font2 = ts->create_font();
ts->font_set_data_ptr(font2, _font_NotoNaskhArabicUI_Regular, _font_NotoNaskhArabicUI_Regular_size);
Vector<RID> font;
font.push_back(font1);
font.push_back(font2);
String test_1 = U"الحمد";
String test_2 = U"الحمد test";
String test_3 = U"test test";
// 7^ 26^
RID ctx;
bool ok;
float width_old, width;
if (ts->has_feature(TextServer::FEATURE_KASHIDA_JUSTIFICATION)) {
ctx = ts->create_shaped_text();
TEST_FAIL_COND(ctx == RID(), "Creating text buffer failed.");
ok = ts->shaped_text_add_string(ctx, test_1, font, 16);
TEST_FAIL_COND(!ok, "Adding text to the buffer failed.");
width_old = ts->shaped_text_get_width(ctx);
width = ts->shaped_text_fit_to_width(ctx, 100, TextServer::JUSTIFICATION_WORD_BOUND);
TEST_FAIL_COND((width != width_old), "Invalid fill width.");
width = ts->shaped_text_fit_to_width(ctx, 100, TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA);
TEST_FAIL_COND((width <= width_old || width > 100), "Invalid fill width.");
ts->free(ctx);
ctx = ts->create_shaped_text();
TEST_FAIL_COND(ctx == RID(), "Creating text buffer failed.");
ok = ts->shaped_text_add_string(ctx, test_2, font, 16);
TEST_FAIL_COND(!ok, "Adding text to the buffer failed.");
width_old = ts->shaped_text_get_width(ctx);
width = ts->shaped_text_fit_to_width(ctx, 100, TextServer::JUSTIFICATION_WORD_BOUND);
TEST_FAIL_COND((width <= width_old || width > 100), "Invalid fill width.");
width = ts->shaped_text_fit_to_width(ctx, 100, TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA);
TEST_FAIL_COND((width <= width_old || width > 100), "Invalid fill width.");
ts->free(ctx);
}
ctx = ts->create_shaped_text();
TEST_FAIL_COND(ctx == RID(), "Creating text buffer failed.");
ok = ts->shaped_text_add_string(ctx, test_3, font, 16);
TEST_FAIL_COND(!ok, "Adding text to the buffer failed.");
width_old = ts->shaped_text_get_width(ctx);
width = ts->shaped_text_fit_to_width(ctx, 100, TextServer::JUSTIFICATION_WORD_BOUND);
TEST_FAIL_COND((width <= width_old || width > 100), "Invalid fill width.");
ts->free(ctx);
for (int j = 0; j < font.size(); j++) {
ts->free(font[j]);
}
font.clear();
}
}
SUBCASE("[TextServer] Strip Diacritics") {
for (int i = 0; i < TextServerManager::get_singleton()->get_interface_count(); i++) {
Ref<TextServer> ts = TextServerManager::get_singleton()->get_interface(i);
TEST_FAIL_COND(ts.is_null(), "Invalid TS interface.");
if (ts->has_feature(TextServer::FEATURE_SHAPING)) {
CHECK(ts->strip_diacritics(U"ٱلسَّلَامُ عَلَيْكُمْ") == U"ٱلسلام عليكم");
}
CHECK(ts->strip_diacritics(U"pêches épinards tomates fraises") == U"peches epinards tomates fraises");
CHECK(ts->strip_diacritics(U"ΆΈΉΊΌΎΏΪΫϓϔ") == U"ΑΕΗΙΟΥΩΙΥΥΥ");
CHECK(ts->strip_diacritics(U"άέήίΐϊΰϋόύώ") == U"αεηιιιυυουω");
CHECK(ts->strip_diacritics(U"ЀЁЃ ЇЌЍӢӤЙ ЎӮӰӲ ӐӒӖӚӜӞ ӦӪ Ӭ Ӵ Ӹ") == U"ЕЕГ ІКИИИИ УУУУ ААЕӘЖЗ ОӨ Э Ч Ы");
CHECK(ts->strip_diacritics(U"ѐёѓ їќѝӣӥй ўӯӱӳ ӑӓӗӛӝӟ ӧӫ ӭ ӵ ӹ") == U"еег ікииии уууу ааеәжз оө э ч ы");
CHECK(ts->strip_diacritics(U"ÀÁÂÃÄÅĀĂĄÇĆĈĊČĎÈÉÊËĒĔĖĘĚĜĞĠĢĤÌÍÎÏĨĪĬĮİĴĶĹĻĽÑŃŅŇŊÒÓÔÕÖØŌŎŐƠŔŖŘŚŜŞŠŢŤÙÚÛÜŨŪŬŮŰŲƯŴÝŶŹŻŽ") == U"AAAAAAAAACCCCCDEEEEEEEEEGGGGHIIIIIIIIIJKLLLNNNNŊOOOOOØOOOORRRSSSSTTUUUUUUUUUUUWYYZZZ");
CHECK(ts->strip_diacritics(U"àáâãäåāăąçćĉċčďèéêëēĕėęěĝğġģĥìíîïĩīĭįĵķĺļľñńņňŋòóôõöøōŏőơŕŗřśŝşšţťùúûüũūŭůűųưŵýÿŷźżž") == U"aaaaaaaaacccccdeeeeeeeeegggghiiiiiiiijklllnnnnŋoooooøoooorrrssssttuuuuuuuuuuuwyyyzzz");
CHECK(ts->strip_diacritics(U"ǍǏȈǑǪǬȌȎȪȬȮȰǓǕǗǙǛȔȖǞǠǺȀȂȦǢǼǦǴǨǸȆȐȒȘȚȞȨ Ḁ ḂḄḆ Ḉ ḊḌḎḐḒ ḔḖḘḚḜ Ḟ Ḡ ḢḤḦḨḪ ḬḮ ḰḲḴ ḶḸḺḼ ḾṀṂ ṄṆṈṊ ṌṎṐṒ ṔṖ ṘṚṜṞ ṠṢṤṦṨ ṪṬṮṰ ṲṴṶṸṺ") == U"AIIOOOOOOOOOUUUUUUUAAAAAAÆÆGGKNERRSTHE A BBB C DDDDD EEEEE F G HHHHH II KKK LLLL MMM NNNN OOOO PP RRRR SSSSS TTTT UUUUU");
CHECK(ts->strip_diacritics(U"ǎǐȉȋǒǫǭȍȏȫȭȯȱǔǖǘǚǜȕȗǟǡǻȁȃȧǣǽǧǵǩǹȇȑȓșțȟȩ ḁ ḃḅḇ ḉ ḋḍḏḑḓ ḟ ḡ ḭḯ ḱḳḵ ḷḹḻḽ ḿṁṃ ṅṇṉṋ ṍṏṑṓ ṗṕ ṙṛṝṟ ṡṣṥṧṩ ṫṭṯṱ ṳṵṷṹṻ") == U"aiiiooooooooouuuuuuuaaaaaaææggknerrsthe a bbb c ddddd f g ii kkk llll mmm nnnn oooo pp rrrr sssss tttt uuuuu");
CHECK(ts->strip_diacritics(U"ṼṾ ẀẂẄẆẈ ẊẌ Ẏ ẐẒẔ") == U"VV WWWWW XX Y ZZZ");
CHECK(ts->strip_diacritics(U"ṽṿ ẁẃẅẇẉ ẋẍ ẏ ẑẓẕ ẖ ẗẘẙẛ") == U"vv wwwww xx y zzz h twys");
}
}
}
}
}; // namespace TestTextServer
#endif // TEST_TEXT_SERVER_H
#endif // TOOLS_ENABLED