virtualx-engine/scene/3d/body_shape.cpp

830 lines
21 KiB
C++
Raw Normal View History

2014-02-10 02:10:30 +01:00
/*************************************************************************/
/* body_shape.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
/* 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 "body_shape.h"
#include "servers/visual_server.h"
#include "scene/resources/sphere_shape.h"
#include "scene/resources/ray_shape.h"
#include "scene/resources/box_shape.h"
#include "scene/resources/capsule_shape.h"
//#include "scene/resources/cylinder_shape.h"
#include "scene/resources/convex_polygon_shape.h"
#include "scene/resources/concave_polygon_shape.h"
#include "scene/resources/height_map_shape.h"
#include "scene/resources/plane_shape.h"
#include "mesh_instance.h"
#include "physics_body.h"
#include "quick_hull.h"
void CollisionShape::_update_body() {
if (get_parent() && get_parent()->cast_to<CollisionObject>())
get_parent()->cast_to<CollisionObject>()->_update_shapes_from_children();
}
void CollisionShape::make_convex_from_brothers() {
Node *p = get_parent();
if (!p)
return;
for(int i=0;i<p->get_child_count();i++) {
Node *n = p->get_child(i);
if (n->cast_to<MeshInstance>()) {
MeshInstance *mi=n->cast_to<MeshInstance>();
Ref<Mesh> m = mi->get_mesh();
if (m.is_valid()) {
Ref<Shape> s = m->create_convex_shape();
set_shape(s);
}
}
}
}
void CollisionShape::_update_indicator() {
while (VisualServer::get_singleton()->mesh_get_surface_count(indicator))
VisualServer::get_singleton()->mesh_remove_surface(indicator,0);
if (shape.is_null())
return;
DVector<Vector3> points;
DVector<Vector3> normals;
VS::PrimitiveType pt = VS::PRIMITIVE_TRIANGLES;
if (shape->cast_to<RayShape>()) {
RayShape *rs = shape->cast_to<RayShape>();
points.push_back(Vector3());
points.push_back(Vector3(0,0,rs->get_length()));
pt = VS::PRIMITIVE_LINES;
} else if (shape->cast_to<SphereShape>()) {
// VisualServer *vs=VisualServer::get_singleton();
SphereShape *shapeptr=shape->cast_to<SphereShape>();
Color col(0.4,1.0,1.0,0.5);
int lats=6;
int lons=12;
float size=shapeptr->get_radius();
for(int i = 1; i <= lats; i++) {
double lat0 = Math_PI * (-0.5 + (double) (i - 1) / lats);
double z0 = Math::sin(lat0);
double zr0 = Math::cos(lat0);
double lat1 = Math_PI * (-0.5 + (double) i / lats);
double z1 = Math::sin(lat1);
double zr1 = Math::cos(lat1);
for(int j = lons; j >= 1; j--) {
double lng0 = 2 * Math_PI * (double) (j - 1) / lons;
double x0 = Math::cos(lng0);
double y0 = Math::sin(lng0);
double lng1 = 2 * Math_PI * (double) (j) / lons;
double x1 = Math::cos(lng1);
double y1 = Math::sin(lng1);
Vector3 v4=Vector3(x0 * zr0, z0, y0 *zr0)*size;
Vector3 v3=Vector3(x0 * zr1, z1, y0 *zr1)*size;
Vector3 v2=Vector3(x1 * zr1, z1, y1 *zr1)*size;
Vector3 v1=Vector3(x1 * zr0, z0, y1 *zr0)*size;
Vector<Vector3> line;
line.push_back(v1);
line.push_back(v2);
line.push_back(v3);
line.push_back(v4);
points.push_back(v1);
points.push_back(v2);
points.push_back(v3);
points.push_back(v1);
points.push_back(v3);
points.push_back(v4);
normals.push_back(v1.normalized());
normals.push_back(v2.normalized());
normals.push_back(v3.normalized());
normals.push_back(v1.normalized());
normals.push_back(v3.normalized());
normals.push_back(v4.normalized());
}
}
} else if (shape->cast_to<BoxShape>()) {
BoxShape *shapeptr=shape->cast_to<BoxShape>();
for (int i=0;i<6;i++) {
Vector3 face_points[4];
for (int j=0;j<4;j++) {
float v[3];
v[0]=1.0;
v[1]=1-2*((j>>1)&1);
v[2]=v[1]*(1-2*(j&1));
for (int k=0;k<3;k++) {
if (i<3)
face_points[j][(i+k)%3]=v[k]*(i>=3?-1:1);
else
face_points[3-j][(i+k)%3]=v[k]*(i>=3?-1:1);
}
}
Vector3 normal;
normal[i%3]=(i>=3?-1:1);
for(int j=0;j<4;j++)
face_points[j]*=shapeptr->get_extents();
points.push_back(face_points[0]);
points.push_back(face_points[1]);
points.push_back(face_points[2]);
points.push_back(face_points[0]);
points.push_back(face_points[2]);
points.push_back(face_points[3]);
for(int n=0;n<6;n++)
normals.push_back(normal);
}
} else if (shape->cast_to<ConvexPolygonShape>()) {
ConvexPolygonShape *shapeptr=shape->cast_to<ConvexPolygonShape>();
Geometry::MeshData md;
QuickHull::build(Variant(shapeptr->get_points()),md);
for(int i=0;i<md.faces.size();i++) {
for(int j=2;j<md.faces[i].indices.size();j++) {
points.push_back(md.vertices[md.faces[i].indices[0]]);
points.push_back(md.vertices[md.faces[i].indices[j-1]]);
points.push_back(md.vertices[md.faces[i].indices[j]]);
normals.push_back(md.faces[i].plane.normal);
normals.push_back(md.faces[i].plane.normal);
normals.push_back(md.faces[i].plane.normal);
}
}
} else if (shape->cast_to<ConcavePolygonShape>()) {
ConcavePolygonShape *shapeptr=shape->cast_to<ConcavePolygonShape>();
points = shapeptr->get_faces();
for(int i=0;i<points.size()/3;i++) {
Vector3 n = Plane( points[i*3+0],points[i*3+1],points[i*3+2] ).normal;
normals.push_back(n);
normals.push_back(n);
normals.push_back(n);
}
} else if (shape->cast_to<CapsuleShape>()) {
CapsuleShape *shapeptr=shape->cast_to<CapsuleShape>();
DVector<Plane> planes = Geometry::build_capsule_planes(shapeptr->get_radius(), shapeptr->get_height()/2.0, 12, Vector3::AXIS_Z);
Geometry::MeshData md = Geometry::build_convex_mesh(planes);
for(int i=0;i<md.faces.size();i++) {
for(int j=2;j<md.faces[i].indices.size();j++) {
points.push_back(md.vertices[md.faces[i].indices[0]]);
points.push_back(md.vertices[md.faces[i].indices[j-1]]);
points.push_back(md.vertices[md.faces[i].indices[j]]);
normals.push_back(md.faces[i].plane.normal);
normals.push_back(md.faces[i].plane.normal);
normals.push_back(md.faces[i].plane.normal);
}
}
} else if (shape->cast_to<PlaneShape>()) {
PlaneShape *shapeptr=shape->cast_to<PlaneShape>();
Plane p = shapeptr->get_plane();
Vector3 n1 = p.get_any_perpendicular_normal();
Vector3 n2 = p.normal.cross(n1).normalized();
Vector3 pface[4]={
p.normal*p.d+n1*100.0+n2*100.0,
p.normal*p.d+n1*100.0+n2*-100.0,
p.normal*p.d+n1*-100.0+n2*-100.0,
p.normal*p.d+n1*-100.0+n2*100.0,
};
points.push_back(pface[0]);
points.push_back(pface[1]);
points.push_back(pface[2]);
points.push_back(pface[0]);
points.push_back(pface[2]);
points.push_back(pface[3]);
normals.push_back(p.normal);
normals.push_back(p.normal);
normals.push_back(p.normal);
normals.push_back(p.normal);
normals.push_back(p.normal);
normals.push_back(p.normal);
}
if (!points.size())
return;
RID material = VisualServer::get_singleton()->fixed_material_create();
VisualServer::get_singleton()->fixed_material_set_param(material,VS::FIXED_MATERIAL_PARAM_DIFFUSE,Color(0,0.6,0.7,0.3));
VisualServer::get_singleton()->fixed_material_set_param(material,VS::FIXED_MATERIAL_PARAM_EMISSION,0.7);
if (normals.size()==0)
VisualServer::get_singleton()->material_set_flag(material,VS::MATERIAL_FLAG_UNSHADED,true);
VisualServer::get_singleton()->material_set_flag(material,VS::MATERIAL_FLAG_DOUBLE_SIDED,true);
Array d;
d.resize(VS::ARRAY_MAX);
d[VS::ARRAY_VERTEX]=points;
if (normals.size())
d[VS::ARRAY_NORMAL]=normals;
VisualServer::get_singleton()->mesh_add_surface(indicator,pt,d);
VisualServer::get_singleton()->mesh_surface_set_material(indicator,0,material,true);
}
void CollisionShape::_add_to_collision_object(Object* p_cshape) {
CollisionObject *co=p_cshape->cast_to<CollisionObject>();
ERR_FAIL_COND(!co);
if (shape.is_valid()) {
co->add_shape(shape,get_transform());
if (trigger)
co->set_shape_as_trigger( co->get_shape_count() -1, true );
}
}
void CollisionShape::_notification(int p_what) {
switch(p_what) {
case NOTIFICATION_ENTER_WORLD: {
indicator_instance = VisualServer::get_singleton()->instance_create2(indicator,get_world()->get_scenario());
} break;
case NOTIFICATION_TRANSFORM_CHANGED: {
VisualServer::get_singleton()->instance_set_transform(indicator_instance,get_global_transform());
if (updating_body) {
_update_body();
}
} break;
case NOTIFICATION_EXIT_WORLD: {
if (indicator_instance.is_valid()) {
VisualServer::get_singleton()->free(indicator_instance);
indicator_instance=RID();
}
} break;
case NOTIFICATION_UNPARENTED: {
if (updating_body)
_update_body();
} break;
case NOTIFICATION_PARENTED: {
if (updating_body)
_update_body();
} break;
}
}
void CollisionShape::resource_changed(RES res) {
update_gizmo();
}
void CollisionShape::_bind_methods() {
//not sure if this should do anything
ObjectTypeDB::bind_method(_MD("resource_changed"),&CollisionShape::resource_changed);
ObjectTypeDB::bind_method(_MD("set_shape","shape"),&CollisionShape::set_shape);
ObjectTypeDB::bind_method(_MD("get_shape"),&CollisionShape::get_shape);
ObjectTypeDB::bind_method(_MD("_add_to_collision_object"),&CollisionShape::_add_to_collision_object);
ObjectTypeDB::bind_method(_MD("set_trigger","enable"),&CollisionShape::set_trigger);
ObjectTypeDB::bind_method(_MD("is_trigger"),&CollisionShape::is_trigger);
ObjectTypeDB::bind_method(_MD("make_convex_from_brothers"),&CollisionShape::make_convex_from_brothers);
ObjectTypeDB::set_method_flags("CollisionShape","make_convex_from_brothers",METHOD_FLAGS_DEFAULT|METHOD_FLAG_EDITOR);
ADD_PROPERTY( PropertyInfo( Variant::OBJECT, "shape", PROPERTY_HINT_RESOURCE_TYPE, "Shape"), _SCS("set_shape"), _SCS("get_shape"));
ADD_PROPERTY(PropertyInfo(Variant::BOOL,"trigger"),_SCS("set_trigger"),_SCS("is_trigger"));
}
void CollisionShape::set_shape(const Ref<Shape> &p_shape) {
if (!shape.is_null())
shape->unregister_owner(this);
shape=p_shape;
if (!shape.is_null())
shape->register_owner(this);
update_gizmo();
if (updating_body)
_update_body();
}
Ref<Shape> CollisionShape::get_shape() const {
return shape;
}
void CollisionShape::set_updating_body(bool p_update) {
updating_body=p_update;
}
bool CollisionShape::is_updating_body() const {
return updating_body;
}
void CollisionShape::set_trigger(bool p_trigger) {
trigger=p_trigger;
if (updating_body)
_update_body();
}
bool CollisionShape::is_trigger() const{
return trigger;
}
CollisionShape::CollisionShape() {
indicator = VisualServer::get_singleton()->mesh_create();
updating_body=true;
trigger=false;
}
CollisionShape::~CollisionShape() {
if (!shape.is_null())
shape->unregister_owner(this);
VisualServer::get_singleton()->free(indicator);
}
#if 0
#include "body_volume.h"
#include "scene/3d/physics_body.h"
#include "geometry.h"
#define ADD_TRIANGLE( m_a, m_b, m_c, m_color)\
{\
Vector<Vector3> points;\
points.resize(3);\
points[0]=m_a;\
points[1]=m_b;\
points[2]=m_c;\
Vector<Color> colors;\
colors.resize(3);\
colors[0]=m_color;\
colors[1]=m_color;\
colors[2]=m_color;\
vs->poly_add_primitive(p_indicator,points,Vector<Vector3>(),colors,Vector<Vector3>());\
}
void CollisionShape::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_SCENE: {
if (get_root_node()->get_editor() && !indicator.is_valid()) {
indicator=VisualServer::get_singleton()->poly_create();
RID mat=VisualServer::get_singleton()->fixed_material_create();
VisualServer::get_singleton()->material_set_flag( mat, VisualServer::MATERIAL_FLAG_UNSHADED, true );
VisualServer::get_singleton()->material_set_flag( mat, VisualServer::MATERIAL_FLAG_WIREFRAME, true );
VisualServer::get_singleton()->material_set_flag( mat, VisualServer::MATERIAL_FLAG_DOUBLE_SIDED, true );
VisualServer::get_singleton()->material_set_line_width( mat, 3 );
VisualServer::get_singleton()->poly_set_material(indicator,mat,true);
update_indicator(indicator);
}
if (indicator.is_valid()) {
indicator_instance=VisualServer::get_singleton()->instance_create2(indicator,get_world()->get_scenario());
VisualServer::get_singleton()->instance_attach_object_instance_ID(indicator_instance,get_instance_ID());
}
volume_changed();
} break;
case NOTIFICATION_EXIT_SCENE: {
if (indicator_instance.is_valid()) {
VisualServer::get_singleton()->free(indicator_instance);
}
volume_changed();
} break;
case NOTIFICATION_TRANSFORM_CHANGED: {
if (indicator_instance.is_valid()) {
VisualServer::get_singleton()->instance_set_transform(indicator_instance,get_global_transform());
}
volume_changed();
} break;
default: {}
}
}
void CollisionShape::volume_changed() {
if (indicator.is_valid())
update_indicator(indicator);
Object *parent=get_parent();
if (!parent)
return;
PhysicsBody *physics_body=parent->cast_to<PhysicsBody>();
ERR_EXPLAIN("CollisionShape parent is not of type PhysicsBody");
ERR_FAIL_COND(!physics_body);
physics_body->recompute_child_volumes();
}
RID CollisionShape::_get_visual_instance_rid() const {
return indicator_instance;
}
void CollisionShape::_bind_methods() {
ObjectTypeDB::bind_method("_get_visual_instance_rid",&CollisionShape::_get_visual_instance_rid);
}
CollisionShape::CollisionShape() {
}
CollisionShape::~CollisionShape() {
if (indicator.is_valid()) {
VisualServer::get_singleton()->free(indicator);
}
}
void CollisionShapeSphere::_set(const String& p_name, const Variant& p_value) {
if (p_name=="radius") {
radius=p_value;
volume_changed();
}
}
Variant CollisionShapeSphere::_get(const String& p_name) const {
if (p_name=="radius") {
return radius;
}
return Variant();
}
void CollisionShapeSphere::_get_property_list( List<PropertyInfo> *p_list) const {
p_list->push_back( PropertyInfo(Variant::REAL,"radius",PROPERTY_HINT_RANGE,"0.01,16384,0.01") );
}
void CollisionShapeSphere::update_indicator(RID p_indicator) {
VisualServer *vs=VisualServer::get_singleton();
vs->poly_clear(p_indicator);
Color col(0.4,1.0,1.0,0.5);
int lats=6;
int lons=12;
float size=radius;
for(int i = 1; i <= lats; i++) {
double lat0 = Math_PI * (-0.5 + (double) (i - 1) / lats);
double z0 = Math::sin(lat0);
double zr0 = Math::cos(lat0);
double lat1 = Math_PI * (-0.5 + (double) i / lats);
double z1 = Math::sin(lat1);
double zr1 = Math::cos(lat1);
for(int j = lons; j >= 1; j--) {
double lng0 = 2 * Math_PI * (double) (j - 1) / lons;
double x0 = Math::cos(lng0);
double y0 = Math::sin(lng0);
double lng1 = 2 * Math_PI * (double) (j) / lons;
double x1 = Math::cos(lng1);
double y1 = Math::sin(lng1);
Vector3 v4=Vector3(x0 * zr0, z0, y0 *zr0)*size;
Vector3 v3=Vector3(x0 * zr1, z1, y0 *zr1)*size;
Vector3 v2=Vector3(x1 * zr1, z1, y1 *zr1)*size;
Vector3 v1=Vector3(x1 * zr0, z0, y1 *zr0)*size;
Vector<Vector3> line;
line.push_back(v1);
line.push_back(v2);
line.push_back(v3);
line.push_back(v4);
Vector<Color> cols;
cols.push_back(col);
cols.push_back(col);
cols.push_back(col);
cols.push_back(col);
VisualServer::get_singleton()->poly_add_primitive(p_indicator,line,Vector<Vector3>(),cols,Vector<Vector3>());
}
}
}
void CollisionShapeSphere::append_to_volume(Ref<Shape> p_volume) {
p_volume->add_sphere_shape(radius,get_transform());
}
CollisionShapeSphere::CollisionShapeSphere() {
radius=1.0;
}
/* BOX */
void CollisionShapeBox::_set(const String& p_name, const Variant& p_value) {
if (p_name=="half_extents") {
half_extents=p_value;
volume_changed();
}
}
Variant CollisionShapeBox::_get(const String& p_name) const {
if (p_name=="half_extents") {
return half_extents;
}
return Variant();
}
void CollisionShapeBox::_get_property_list( List<PropertyInfo> *p_list) const {
p_list->push_back( PropertyInfo(Variant::VECTOR3,"half_extents" ) );
}
void CollisionShapeBox::update_indicator(RID p_indicator) {
VisualServer *vs=VisualServer::get_singleton();
vs->poly_clear(p_indicator);
Color col(0.4,1.0,1.0,0.5);
for (int i=0;i<6;i++) {
Vector3 face_points[4];
for (int j=0;j<4;j++) {
float v[3];
v[0]=1.0;
v[1]=1-2*((j>>1)&1);
v[2]=v[1]*(1-2*(j&1));
for (int k=0;k<3;k++) {
if (i<3)
face_points[j][(i+k)%3]=v[k]*(i>=3?-1:1);
else
face_points[3-j][(i+k)%3]=v[k]*(i>=3?-1:1);
}
}
for(int j=0;j<4;j++)
face_points[i]*=half_extents;
ADD_TRIANGLE(face_points[0],face_points[1],face_points[2],col);
ADD_TRIANGLE(face_points[2],face_points[3],face_points[0],col);
}
}
void CollisionShapeBox::append_to_volume(Ref<Shape> p_volume) {
p_volume->add_box_shape(half_extents,get_transform());
}
CollisionShapeBox::CollisionShapeBox() {
half_extents=Vector3(1,1,1);
}
/* CYLINDER */
void CollisionShapeCylinder::_set(const String& p_name, const Variant& p_value) {
if (p_name=="radius") {
radius=p_value;
volume_changed();
}
if (p_name=="height") {
height=p_value;
volume_changed();
}
}
Variant CollisionShapeCylinder::_get(const String& p_name) const {
if (p_name=="radius") {
return radius;
}
if (p_name=="height") {
return height;
}
return Variant();
}
void CollisionShapeCylinder::_get_property_list( List<PropertyInfo> *p_list) const {
p_list->push_back( PropertyInfo(Variant::REAL,"radius",PROPERTY_HINT_RANGE,"0.01,16384,0.01") );
p_list->push_back( PropertyInfo(Variant::REAL,"height",PROPERTY_HINT_RANGE,"0.01,16384,0.01") );
}
void CollisionShapeCylinder::update_indicator(RID p_indicator) {
VisualServer *vs=VisualServer::get_singleton();
vs->poly_clear(p_indicator);
Color col(0.4,1.0,1.0,0.5);
DVector<Plane> planes = Geometry::build_cylinder_planes(radius, height, 12, Vector3::AXIS_Z);
Geometry::MeshData md = Geometry::build_convex_mesh(planes);
for(int i=0;i<md.faces.size();i++) {
for(int j=2;j<md.faces[i].indices.size();j++) {
ADD_TRIANGLE(md.vertices[md.faces[i].indices[0]],md.vertices[md.faces[i].indices[j-1]],md.vertices[md.faces[i].indices[j]],col);
}
}
}
void CollisionShapeCylinder::append_to_volume(Ref<Shape> p_volume) {
p_volume->add_cylinder_shape(radius,height*2.0,get_transform());
}
CollisionShapeCylinder::CollisionShapeCylinder() {
height=1;
radius=1;
}
/* CAPSULE */
void CollisionShapeCapsule::_set(const String& p_name, const Variant& p_value) {
if (p_name=="radius") {
radius=p_value;
volume_changed();
}
if (p_name=="height") {
height=p_value;
volume_changed();
}
}
Variant CollisionShapeCapsule::_get(const String& p_name) const {
if (p_name=="radius") {
return radius;
}
if (p_name=="height") {
return height;
}
return Variant();
}
void CollisionShapeCapsule::_get_property_list( List<PropertyInfo> *p_list) const {
p_list->push_back( PropertyInfo(Variant::REAL,"radius",PROPERTY_HINT_RANGE,"0.01,16384,0.01") );
p_list->push_back( PropertyInfo(Variant::REAL,"height",PROPERTY_HINT_RANGE,"0.01,16384,0.01") );
}
void CollisionShapeCapsule::update_indicator(RID p_indicator) {
VisualServer *vs=VisualServer::get_singleton();
vs->poly_clear(p_indicator);
Color col(0.4,1.0,1.0,0.5);
DVector<Plane> planes = Geometry::build_capsule_planes(radius, height, 12, 3, Vector3::AXIS_Z);
Geometry::MeshData md = Geometry::build_convex_mesh(planes);
for(int i=0;i<md.faces.size();i++) {
for(int j=2;j<md.faces[i].indices.size();j++) {
ADD_TRIANGLE(md.vertices[md.faces[i].indices[0]],md.vertices[md.faces[i].indices[j-1]],md.vertices[md.faces[i].indices[j]],col);
}
}
}
void CollisionShapeCapsule::append_to_volume(Ref<Shape> p_volume) {
p_volume->add_capsule_shape(radius,height,get_transform());
}
CollisionShapeCapsule::CollisionShapeCapsule() {
height=1;
radius=1;
}
#endif