2018-05-16 17:19:33 +00:00
|
|
|
/*************************************************************************/
|
|
|
|
/* csg_shape.h */
|
|
|
|
/*************************************************************************/
|
|
|
|
/* This file is part of: */
|
|
|
|
/* GODOT ENGINE */
|
|
|
|
/* https://godotengine.org */
|
|
|
|
/*************************************************************************/
|
2020-01-01 10:16:22 +00:00
|
|
|
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
|
|
|
|
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
|
2018-05-16 17:19:33 +00:00
|
|
|
/* */
|
|
|
|
/* 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. */
|
|
|
|
/*************************************************************************/
|
|
|
|
|
2018-04-28 00:52:15 +00:00
|
|
|
#ifndef CSG_SHAPE_H
|
|
|
|
#define CSG_SHAPE_H
|
|
|
|
|
|
|
|
#define CSGJS_HEADER_ONLY
|
|
|
|
|
|
|
|
#include "csg.h"
|
2020-03-26 21:49:16 +00:00
|
|
|
#include "scene/3d/visual_instance_3d.h"
|
|
|
|
#include "scene/resources/concave_polygon_shape_3d.h"
|
2018-11-16 11:56:12 +00:00
|
|
|
#include "thirdparty/misc/mikktspace.h"
|
2018-04-28 00:52:15 +00:00
|
|
|
|
2020-03-26 21:49:16 +00:00
|
|
|
class CSGShape3D : public GeometryInstance3D {
|
|
|
|
GDCLASS(CSGShape3D, GeometryInstance3D);
|
2018-04-28 00:52:15 +00:00
|
|
|
|
|
|
|
public:
|
|
|
|
enum Operation {
|
|
|
|
OPERATION_UNION,
|
|
|
|
OPERATION_INTERSECTION,
|
|
|
|
OPERATION_SUBTRACTION,
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
private:
|
|
|
|
Operation operation;
|
2020-03-26 21:49:16 +00:00
|
|
|
CSGShape3D *parent;
|
2018-04-28 00:52:15 +00:00
|
|
|
|
|
|
|
CSGBrush *brush;
|
|
|
|
|
|
|
|
AABB node_aabb;
|
|
|
|
|
|
|
|
bool dirty;
|
2018-04-28 15:33:23 +00:00
|
|
|
float snap;
|
2018-04-28 00:52:15 +00:00
|
|
|
|
|
|
|
bool use_collision;
|
2018-10-12 23:58:58 +00:00
|
|
|
uint32_t collision_layer;
|
|
|
|
uint32_t collision_mask;
|
2020-03-26 21:49:16 +00:00
|
|
|
Ref<ConcavePolygonShape3D> root_collision_shape;
|
2018-04-28 00:52:15 +00:00
|
|
|
RID root_collision_instance;
|
|
|
|
|
2018-11-16 11:56:12 +00:00
|
|
|
bool calculate_tangents;
|
|
|
|
|
2018-04-28 00:52:15 +00:00
|
|
|
Ref<ArrayMesh> root_mesh;
|
|
|
|
|
|
|
|
struct Vector3Hasher {
|
|
|
|
_ALWAYS_INLINE_ uint32_t hash(const Vector3 &p_vec3) const {
|
|
|
|
uint32_t h = hash_djb2_one_float(p_vec3.x);
|
|
|
|
h = hash_djb2_one_float(p_vec3.y, h);
|
|
|
|
h = hash_djb2_one_float(p_vec3.z, h);
|
|
|
|
return h;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
struct ShapeUpdateSurface {
|
2020-02-17 21:06:54 +00:00
|
|
|
Vector<Vector3> vertices;
|
|
|
|
Vector<Vector3> normals;
|
|
|
|
Vector<Vector2> uvs;
|
|
|
|
Vector<float> tans;
|
2018-04-28 00:52:15 +00:00
|
|
|
Ref<Material> material;
|
|
|
|
int last_added;
|
|
|
|
|
2020-02-17 21:06:54 +00:00
|
|
|
Vector3 *verticesw;
|
|
|
|
Vector3 *normalsw;
|
|
|
|
Vector2 *uvsw;
|
|
|
|
float *tansw;
|
2018-04-28 00:52:15 +00:00
|
|
|
};
|
|
|
|
|
2018-11-16 11:56:12 +00:00
|
|
|
//mikktspace callbacks
|
|
|
|
static int mikktGetNumFaces(const SMikkTSpaceContext *pContext);
|
|
|
|
static int mikktGetNumVerticesOfFace(const SMikkTSpaceContext *pContext, const int iFace);
|
|
|
|
static void mikktGetPosition(const SMikkTSpaceContext *pContext, float fvPosOut[], const int iFace, const int iVert);
|
|
|
|
static void mikktGetNormal(const SMikkTSpaceContext *pContext, float fvNormOut[], const int iFace, const int iVert);
|
|
|
|
static void mikktGetTexCoord(const SMikkTSpaceContext *pContext, float fvTexcOut[], const int iFace, const int iVert);
|
|
|
|
static void mikktSetTSpaceDefault(const SMikkTSpaceContext *pContext, const float fvTangent[], const float fvBiTangent[], const float fMagS, const float fMagT,
|
|
|
|
const tbool bIsOrientationPreserving, const int iFace, const int iVert);
|
|
|
|
|
2018-04-28 00:52:15 +00:00
|
|
|
void _update_shape();
|
|
|
|
|
|
|
|
protected:
|
|
|
|
void _notification(int p_what);
|
2018-04-28 15:33:23 +00:00
|
|
|
virtual CSGBrush *_build_brush() = 0;
|
2018-04-28 00:52:15 +00:00
|
|
|
void _make_dirty();
|
|
|
|
|
|
|
|
static void _bind_methods();
|
|
|
|
|
2020-03-26 21:49:16 +00:00
|
|
|
friend class CSGCombiner3D;
|
2018-04-28 00:52:15 +00:00
|
|
|
CSGBrush *_get_brush();
|
|
|
|
|
|
|
|
virtual void _validate_property(PropertyInfo &property) const;
|
|
|
|
|
2019-06-13 11:29:43 +00:00
|
|
|
public:
|
2019-01-27 21:28:41 +00:00
|
|
|
Array get_meshes() const;
|
|
|
|
|
2018-04-28 00:52:15 +00:00
|
|
|
void set_operation(Operation p_operation);
|
|
|
|
Operation get_operation() const;
|
|
|
|
|
2020-02-17 21:06:54 +00:00
|
|
|
virtual Vector<Vector3> get_brush_faces();
|
2018-04-28 00:52:15 +00:00
|
|
|
|
|
|
|
virtual AABB get_aabb() const;
|
2020-02-17 21:06:54 +00:00
|
|
|
virtual Vector<Face3> get_faces(uint32_t p_usage_flags) const;
|
2018-04-28 00:52:15 +00:00
|
|
|
|
|
|
|
void set_use_collision(bool p_enable);
|
|
|
|
bool is_using_collision() const;
|
|
|
|
|
2018-10-12 23:58:58 +00:00
|
|
|
void set_collision_layer(uint32_t p_layer);
|
|
|
|
uint32_t get_collision_layer() const;
|
|
|
|
|
|
|
|
void set_collision_mask(uint32_t p_mask);
|
|
|
|
uint32_t get_collision_mask() const;
|
|
|
|
|
|
|
|
void set_collision_layer_bit(int p_bit, bool p_value);
|
|
|
|
bool get_collision_layer_bit(int p_bit) const;
|
|
|
|
|
|
|
|
void set_collision_mask_bit(int p_bit, bool p_value);
|
|
|
|
bool get_collision_mask_bit(int p_bit) const;
|
|
|
|
|
2018-04-28 15:33:23 +00:00
|
|
|
void set_snap(float p_snap);
|
|
|
|
float get_snap() const;
|
|
|
|
|
2018-11-16 11:56:12 +00:00
|
|
|
void set_calculate_tangents(bool p_calculate_tangents);
|
|
|
|
bool is_calculating_tangents() const;
|
|
|
|
|
2018-04-28 00:52:15 +00:00
|
|
|
bool is_root_shape() const;
|
2020-03-26 21:49:16 +00:00
|
|
|
CSGShape3D();
|
|
|
|
~CSGShape3D();
|
2018-04-28 00:52:15 +00:00
|
|
|
};
|
|
|
|
|
2020-03-26 21:49:16 +00:00
|
|
|
VARIANT_ENUM_CAST(CSGShape3D::Operation)
|
2018-04-28 00:52:15 +00:00
|
|
|
|
2020-03-26 21:49:16 +00:00
|
|
|
class CSGCombiner3D : public CSGShape3D {
|
|
|
|
GDCLASS(CSGCombiner3D, CSGShape3D);
|
2019-03-19 18:35:57 +00:00
|
|
|
|
2018-04-28 00:52:15 +00:00
|
|
|
private:
|
2018-04-28 15:33:23 +00:00
|
|
|
virtual CSGBrush *_build_brush();
|
2018-04-28 00:52:15 +00:00
|
|
|
|
|
|
|
public:
|
2020-03-26 21:49:16 +00:00
|
|
|
CSGCombiner3D();
|
2018-04-28 00:52:15 +00:00
|
|
|
};
|
|
|
|
|
2020-03-26 21:49:16 +00:00
|
|
|
class CSGPrimitive3D : public CSGShape3D {
|
|
|
|
GDCLASS(CSGPrimitive3D, CSGShape3D);
|
2018-04-28 00:52:15 +00:00
|
|
|
|
|
|
|
private:
|
|
|
|
bool invert_faces;
|
|
|
|
|
|
|
|
protected:
|
2020-03-17 06:33:00 +00:00
|
|
|
CSGBrush *_create_brush_from_arrays(const Vector<Vector3> &p_vertices, const Vector<Vector2> &p_uv, const Vector<bool> &p_smooth, const Vector<Ref<Material>> &p_materials);
|
2018-04-28 00:52:15 +00:00
|
|
|
static void _bind_methods();
|
|
|
|
|
|
|
|
public:
|
|
|
|
void set_invert_faces(bool p_invert);
|
|
|
|
bool is_inverting_faces();
|
|
|
|
|
2020-03-26 21:49:16 +00:00
|
|
|
CSGPrimitive3D();
|
2018-04-28 00:52:15 +00:00
|
|
|
};
|
|
|
|
|
2020-03-26 21:49:16 +00:00
|
|
|
class CSGMesh3D : public CSGPrimitive3D {
|
|
|
|
GDCLASS(CSGMesh3D, CSGPrimitive3D);
|
2018-04-28 00:52:15 +00:00
|
|
|
|
2018-04-28 15:33:23 +00:00
|
|
|
virtual CSGBrush *_build_brush();
|
2018-04-28 00:52:15 +00:00
|
|
|
|
|
|
|
Ref<Mesh> mesh;
|
2019-04-11 20:20:09 +00:00
|
|
|
Ref<Material> material;
|
2018-04-28 00:52:15 +00:00
|
|
|
|
|
|
|
void _mesh_changed();
|
|
|
|
|
|
|
|
protected:
|
|
|
|
static void _bind_methods();
|
|
|
|
|
|
|
|
public:
|
|
|
|
void set_mesh(const Ref<Mesh> &p_mesh);
|
|
|
|
Ref<Mesh> get_mesh();
|
2019-04-11 20:20:09 +00:00
|
|
|
|
|
|
|
void set_material(const Ref<Material> &p_material);
|
|
|
|
Ref<Material> get_material() const;
|
2018-04-28 00:52:15 +00:00
|
|
|
};
|
|
|
|
|
2020-03-26 21:49:16 +00:00
|
|
|
class CSGSphere3D : public CSGPrimitive3D {
|
2018-04-28 00:52:15 +00:00
|
|
|
|
2020-03-26 21:49:16 +00:00
|
|
|
GDCLASS(CSGSphere3D, CSGPrimitive3D);
|
2018-04-28 15:33:23 +00:00
|
|
|
virtual CSGBrush *_build_brush();
|
2018-04-28 00:52:15 +00:00
|
|
|
|
|
|
|
Ref<Material> material;
|
|
|
|
bool smooth_faces;
|
|
|
|
float radius;
|
|
|
|
int radial_segments;
|
|
|
|
int rings;
|
|
|
|
|
|
|
|
protected:
|
|
|
|
static void _bind_methods();
|
|
|
|
|
|
|
|
public:
|
|
|
|
void set_radius(const float p_radius);
|
|
|
|
float get_radius() const;
|
|
|
|
|
|
|
|
void set_radial_segments(const int p_radial_segments);
|
|
|
|
int get_radial_segments() const;
|
|
|
|
|
|
|
|
void set_rings(const int p_rings);
|
|
|
|
int get_rings() const;
|
|
|
|
|
|
|
|
void set_material(const Ref<Material> &p_material);
|
|
|
|
Ref<Material> get_material() const;
|
|
|
|
|
|
|
|
void set_smooth_faces(bool p_smooth_faces);
|
|
|
|
bool get_smooth_faces() const;
|
|
|
|
|
2020-03-26 21:49:16 +00:00
|
|
|
CSGSphere3D();
|
2018-04-28 00:52:15 +00:00
|
|
|
};
|
|
|
|
|
2020-03-26 21:49:16 +00:00
|
|
|
class CSGBox3D : public CSGPrimitive3D {
|
2018-04-28 00:52:15 +00:00
|
|
|
|
2020-03-26 21:49:16 +00:00
|
|
|
GDCLASS(CSGBox3D, CSGPrimitive3D);
|
2018-04-28 15:33:23 +00:00
|
|
|
virtual CSGBrush *_build_brush();
|
2018-04-28 00:52:15 +00:00
|
|
|
|
|
|
|
Ref<Material> material;
|
|
|
|
float width;
|
|
|
|
float height;
|
|
|
|
float depth;
|
|
|
|
|
|
|
|
protected:
|
|
|
|
static void _bind_methods();
|
|
|
|
|
|
|
|
public:
|
|
|
|
void set_width(const float p_width);
|
|
|
|
float get_width() const;
|
|
|
|
|
|
|
|
void set_height(const float p_height);
|
|
|
|
float get_height() const;
|
|
|
|
|
|
|
|
void set_depth(const float p_depth);
|
|
|
|
float get_depth() const;
|
|
|
|
|
|
|
|
void set_material(const Ref<Material> &p_material);
|
|
|
|
Ref<Material> get_material() const;
|
|
|
|
|
2020-03-26 21:49:16 +00:00
|
|
|
CSGBox3D();
|
2018-04-28 00:52:15 +00:00
|
|
|
};
|
|
|
|
|
2020-03-26 21:49:16 +00:00
|
|
|
class CSGCylinder3D : public CSGPrimitive3D {
|
2018-04-28 00:52:15 +00:00
|
|
|
|
2020-03-26 21:49:16 +00:00
|
|
|
GDCLASS(CSGCylinder3D, CSGPrimitive3D);
|
2018-04-28 15:33:23 +00:00
|
|
|
virtual CSGBrush *_build_brush();
|
2018-04-28 00:52:15 +00:00
|
|
|
|
|
|
|
Ref<Material> material;
|
|
|
|
float radius;
|
|
|
|
float height;
|
|
|
|
int sides;
|
|
|
|
bool cone;
|
|
|
|
bool smooth_faces;
|
|
|
|
|
|
|
|
protected:
|
|
|
|
static void _bind_methods();
|
|
|
|
|
|
|
|
public:
|
|
|
|
void set_radius(const float p_radius);
|
|
|
|
float get_radius() const;
|
|
|
|
|
|
|
|
void set_height(const float p_height);
|
|
|
|
float get_height() const;
|
|
|
|
|
|
|
|
void set_sides(const int p_sides);
|
|
|
|
int get_sides() const;
|
|
|
|
|
|
|
|
void set_cone(const bool p_cone);
|
|
|
|
bool is_cone() const;
|
|
|
|
|
|
|
|
void set_smooth_faces(bool p_smooth_faces);
|
|
|
|
bool get_smooth_faces() const;
|
|
|
|
|
|
|
|
void set_material(const Ref<Material> &p_material);
|
|
|
|
Ref<Material> get_material() const;
|
|
|
|
|
2020-03-26 21:49:16 +00:00
|
|
|
CSGCylinder3D();
|
2018-04-28 00:52:15 +00:00
|
|
|
};
|
|
|
|
|
2020-03-26 21:49:16 +00:00
|
|
|
class CSGTorus3D : public CSGPrimitive3D {
|
2018-04-28 00:52:15 +00:00
|
|
|
|
2020-03-26 21:49:16 +00:00
|
|
|
GDCLASS(CSGTorus3D, CSGPrimitive3D);
|
2018-04-28 15:33:23 +00:00
|
|
|
virtual CSGBrush *_build_brush();
|
2018-04-28 00:52:15 +00:00
|
|
|
|
|
|
|
Ref<Material> material;
|
|
|
|
float inner_radius;
|
|
|
|
float outer_radius;
|
|
|
|
int sides;
|
|
|
|
int ring_sides;
|
|
|
|
bool smooth_faces;
|
|
|
|
|
|
|
|
protected:
|
|
|
|
static void _bind_methods();
|
|
|
|
|
|
|
|
public:
|
|
|
|
void set_inner_radius(const float p_inner_radius);
|
|
|
|
float get_inner_radius() const;
|
|
|
|
|
|
|
|
void set_outer_radius(const float p_outer_radius);
|
|
|
|
float get_outer_radius() const;
|
|
|
|
|
|
|
|
void set_sides(const int p_sides);
|
|
|
|
int get_sides() const;
|
|
|
|
|
|
|
|
void set_ring_sides(const int p_ring_sides);
|
|
|
|
int get_ring_sides() const;
|
|
|
|
|
|
|
|
void set_smooth_faces(bool p_smooth_faces);
|
|
|
|
bool get_smooth_faces() const;
|
|
|
|
|
|
|
|
void set_material(const Ref<Material> &p_material);
|
|
|
|
Ref<Material> get_material() const;
|
|
|
|
|
2020-03-26 21:49:16 +00:00
|
|
|
CSGTorus3D();
|
2018-04-28 00:52:15 +00:00
|
|
|
};
|
|
|
|
|
2020-03-26 21:49:16 +00:00
|
|
|
class CSGPolygon3D : public CSGPrimitive3D {
|
2018-04-28 00:52:15 +00:00
|
|
|
|
2020-03-26 21:49:16 +00:00
|
|
|
GDCLASS(CSGPolygon3D, CSGPrimitive3D);
|
2018-04-28 00:52:15 +00:00
|
|
|
|
|
|
|
public:
|
|
|
|
enum Mode {
|
|
|
|
MODE_DEPTH,
|
|
|
|
MODE_SPIN,
|
|
|
|
MODE_PATH
|
|
|
|
};
|
|
|
|
|
|
|
|
enum PathRotation {
|
|
|
|
PATH_ROTATION_POLYGON,
|
|
|
|
PATH_ROTATION_PATH,
|
|
|
|
PATH_ROTATION_PATH_FOLLOW,
|
|
|
|
};
|
|
|
|
|
|
|
|
private:
|
2018-04-28 15:33:23 +00:00
|
|
|
virtual CSGBrush *_build_brush();
|
2018-04-28 00:52:15 +00:00
|
|
|
|
|
|
|
Vector<Vector2> polygon;
|
|
|
|
Ref<Material> material;
|
|
|
|
|
|
|
|
Mode mode;
|
|
|
|
|
|
|
|
float depth;
|
|
|
|
|
|
|
|
float spin_degrees;
|
|
|
|
int spin_sides;
|
|
|
|
|
|
|
|
NodePath path_node;
|
|
|
|
float path_interval;
|
|
|
|
PathRotation path_rotation;
|
2018-07-03 11:21:36 +00:00
|
|
|
bool path_local;
|
2018-04-28 00:52:15 +00:00
|
|
|
|
|
|
|
Node *path_cache;
|
|
|
|
|
|
|
|
bool smooth_faces;
|
2018-07-03 11:21:36 +00:00
|
|
|
bool path_continuous_u;
|
|
|
|
bool path_joined;
|
2018-04-28 00:52:15 +00:00
|
|
|
|
|
|
|
bool _is_editable_3d_polygon() const;
|
|
|
|
bool _has_editable_3d_polygon_no_depth() const;
|
|
|
|
|
|
|
|
void _path_changed();
|
|
|
|
void _path_exited();
|
|
|
|
|
|
|
|
protected:
|
|
|
|
static void _bind_methods();
|
|
|
|
virtual void _validate_property(PropertyInfo &property) const;
|
|
|
|
void _notification(int p_what);
|
|
|
|
|
|
|
|
public:
|
|
|
|
void set_polygon(const Vector<Vector2> &p_polygon);
|
|
|
|
Vector<Vector2> get_polygon() const;
|
|
|
|
|
|
|
|
void set_mode(Mode p_mode);
|
|
|
|
Mode get_mode() const;
|
|
|
|
|
|
|
|
void set_depth(float p_depth);
|
|
|
|
float get_depth() const;
|
|
|
|
|
|
|
|
void set_spin_degrees(float p_spin_degrees);
|
|
|
|
float get_spin_degrees() const;
|
|
|
|
|
2019-06-26 13:08:25 +00:00
|
|
|
void set_spin_sides(int p_spin_sides);
|
2018-04-28 00:52:15 +00:00
|
|
|
int get_spin_sides() const;
|
|
|
|
|
|
|
|
void set_path_node(const NodePath &p_path);
|
|
|
|
NodePath get_path_node() const;
|
|
|
|
|
|
|
|
void set_path_interval(float p_interval);
|
|
|
|
float get_path_interval() const;
|
|
|
|
|
|
|
|
void set_path_rotation(PathRotation p_rotation);
|
|
|
|
PathRotation get_path_rotation() const;
|
|
|
|
|
2018-07-03 11:21:36 +00:00
|
|
|
void set_path_local(bool p_enable);
|
|
|
|
bool is_path_local() const;
|
|
|
|
|
|
|
|
void set_path_continuous_u(bool p_enable);
|
|
|
|
bool is_path_continuous_u() const;
|
|
|
|
|
|
|
|
void set_path_joined(bool p_enable);
|
|
|
|
bool is_path_joined() const;
|
|
|
|
|
2018-04-28 00:52:15 +00:00
|
|
|
void set_smooth_faces(bool p_smooth_faces);
|
|
|
|
bool get_smooth_faces() const;
|
|
|
|
|
|
|
|
void set_material(const Ref<Material> &p_material);
|
|
|
|
Ref<Material> get_material() const;
|
|
|
|
|
2020-03-26 21:49:16 +00:00
|
|
|
CSGPolygon3D();
|
2018-04-28 00:52:15 +00:00
|
|
|
};
|
|
|
|
|
2020-03-26 21:49:16 +00:00
|
|
|
VARIANT_ENUM_CAST(CSGPolygon3D::Mode)
|
|
|
|
VARIANT_ENUM_CAST(CSGPolygon3D::PathRotation)
|
2018-04-28 00:52:15 +00:00
|
|
|
|
|
|
|
#endif // CSG_SHAPE_H
|