Merge pull request #23743 from BastiaanOlij/csg_mikkt_tangent

Adding mikkt tangent support to CSG objects
This commit is contained in:
Juan Linietsky 2018-11-16 23:24:52 +01:00 committed by GitHub
commit 6ed1be9637
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 145 additions and 3 deletions

View File

@ -159,6 +159,74 @@ CSGBrush *CSGShape::_get_brush() {
return brush; return brush;
} }
int CSGShape::mikktGetNumFaces(const SMikkTSpaceContext *pContext) {
ShapeUpdateSurface &surface = *((ShapeUpdateSurface *)pContext->m_pUserData);
return surface.vertices.size() / 3;
}
int CSGShape::mikktGetNumVerticesOfFace(const SMikkTSpaceContext *pContext, const int iFace) {
// always 3
return 3;
}
void CSGShape::mikktGetPosition(const SMikkTSpaceContext *pContext, float fvPosOut[], const int iFace, const int iVert) {
ShapeUpdateSurface &surface = *((ShapeUpdateSurface *)pContext->m_pUserData);
Vector3 v = surface.verticesw[iFace * 3 + iVert];
fvPosOut[0] = v.x;
fvPosOut[1] = v.y;
fvPosOut[2] = v.z;
}
void CSGShape::mikktGetNormal(const SMikkTSpaceContext *pContext, float fvNormOut[], const int iFace, const int iVert) {
ShapeUpdateSurface &surface = *((ShapeUpdateSurface *)pContext->m_pUserData);
Vector3 n = surface.normalsw[iFace * 3 + iVert];
fvNormOut[0] = n.x;
fvNormOut[1] = n.y;
fvNormOut[2] = n.z;
}
void CSGShape::mikktGetTexCoord(const SMikkTSpaceContext *pContext, float fvTexcOut[], const int iFace, const int iVert) {
ShapeUpdateSurface &surface = *((ShapeUpdateSurface *)pContext->m_pUserData);
Vector2 t = surface.uvsw[iFace * 3 + iVert];
fvTexcOut[0] = t.x;
fvTexcOut[1] = t.y;
}
void CSGShape::mikktSetTSpaceBasic(const SMikkTSpaceContext *pContext, const float fvTangent[], const float fSign, const int iFace, const int iVert) {
ShapeUpdateSurface &surface = *((ShapeUpdateSurface *)pContext->m_pUserData);
int i = (iFace * 3 + iVert) * 4;
// Godot seems to want the tangent flipped because our handedness is reversed..
surface.tansw[i++] = -fvTangent[0];
surface.tansw[i++] = -fvTangent[1];
surface.tansw[i++] = -fvTangent[2];
surface.tansw[i++] = fSign;
}
void CSGShape::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) {
ShapeUpdateSurface &surface = *((ShapeUpdateSurface *)pContext->m_pUserData);
int i = iFace * 3 + iVert;
Vector3 normal = surface.normalsw[i];
Vector3 tangent = Vector3(fvTangent[0], fvTangent[1], fvTangent[2]);
Vector3 bitangent = Vector3(fvBiTangent[0], fvBiTangent[1], fvBiTangent[2]);
float d = bitangent.dot(normal.cross(tangent));
// Godot seems to want the tangent flipped because our handedness is reversed..
i *= 4;
surface.tansw[i++] = -tangent.x;
surface.tansw[i++] = -tangent.y;
surface.tansw[i++] = -tangent.z;
surface.tansw[i++] = d < 0 ? -1 : 1;
}
void CSGShape::_update_shape() { void CSGShape::_update_shape() {
if (parent) if (parent)
@ -211,6 +279,9 @@ void CSGShape::_update_shape() {
surfaces.write[i].vertices.resize(face_count[i] * 3); surfaces.write[i].vertices.resize(face_count[i] * 3);
surfaces.write[i].normals.resize(face_count[i] * 3); surfaces.write[i].normals.resize(face_count[i] * 3);
surfaces.write[i].uvs.resize(face_count[i] * 3); surfaces.write[i].uvs.resize(face_count[i] * 3);
if (calculate_tangents) {
surfaces.write[i].tans.resize(face_count[i] * 3 * 4);
}
surfaces.write[i].last_added = 0; surfaces.write[i].last_added = 0;
if (i != surfaces.size() - 1) { if (i != surfaces.size() - 1) {
@ -220,6 +291,9 @@ void CSGShape::_update_shape() {
surfaces.write[i].verticesw = surfaces.write[i].vertices.write(); surfaces.write[i].verticesw = surfaces.write[i].vertices.write();
surfaces.write[i].normalsw = surfaces.write[i].normals.write(); surfaces.write[i].normalsw = surfaces.write[i].normals.write();
surfaces.write[i].uvsw = surfaces.write[i].uvs.write(); surfaces.write[i].uvsw = surfaces.write[i].uvs.write();
if (calculate_tangents) {
surfaces.write[i].tansw = surfaces.write[i].tans.write();
}
} }
//fill arrays //fill arrays
@ -274,9 +348,19 @@ void CSGShape::_update_shape() {
normal = -normal; normal = -normal;
} }
surfaces[idx].verticesw[last + order[j]] = v; int k = last + order[j];
surfaces[idx].uvsw[last + order[j]] = n->faces[i].uvs[j]; surfaces[idx].verticesw[k] = v;
surfaces[idx].normalsw[last + order[j]] = normal; surfaces[idx].uvsw[k] = n->faces[i].uvs[j];
surfaces[idx].normalsw[k] = normal;
if (calculate_tangents) {
// zero out our tangents for now
k *= 4;
surfaces[idx].tansw[k++] = 0.0;
surfaces[idx].tansw[k++] = 0.0;
surfaces[idx].tansw[k++] = 0.0;
surfaces[idx].tansw[k++] = 0.0;
}
} }
surfaces.write[idx].last_added += 3; surfaces.write[idx].last_added += 3;
@ -287,20 +371,43 @@ void CSGShape::_update_shape() {
//create surfaces //create surfaces
for (int i = 0; i < surfaces.size(); i++) { for (int i = 0; i < surfaces.size(); i++) {
// calculate tangents for this surface
bool have_tangents = calculate_tangents;
if (have_tangents) {
SMikkTSpaceInterface mkif;
mkif.m_getNormal = mikktGetNormal;
mkif.m_getNumFaces = mikktGetNumFaces;
mkif.m_getNumVerticesOfFace = mikktGetNumVerticesOfFace;
mkif.m_getPosition = mikktGetPosition;
mkif.m_getTexCoord = mikktGetTexCoord;
mkif.m_setTSpace = mikktSetTSpaceDefault;
mkif.m_setTSpaceBasic = NULL;
SMikkTSpaceContext msc;
msc.m_pInterface = &mkif;
msc.m_pUserData = &surfaces.write[i];
have_tangents = genTangSpaceDefault(&msc);
}
// unset write access
surfaces.write[i].verticesw = PoolVector<Vector3>::Write(); surfaces.write[i].verticesw = PoolVector<Vector3>::Write();
surfaces.write[i].normalsw = PoolVector<Vector3>::Write(); surfaces.write[i].normalsw = PoolVector<Vector3>::Write();
surfaces.write[i].uvsw = PoolVector<Vector2>::Write(); surfaces.write[i].uvsw = PoolVector<Vector2>::Write();
surfaces.write[i].tansw = PoolVector<float>::Write();
if (surfaces[i].last_added == 0) if (surfaces[i].last_added == 0)
continue; continue;
// and convert to surface array
Array array; Array array;
array.resize(Mesh::ARRAY_MAX); array.resize(Mesh::ARRAY_MAX);
array[Mesh::ARRAY_VERTEX] = surfaces[i].vertices; array[Mesh::ARRAY_VERTEX] = surfaces[i].vertices;
array[Mesh::ARRAY_NORMAL] = surfaces[i].normals; array[Mesh::ARRAY_NORMAL] = surfaces[i].normals;
array[Mesh::ARRAY_TEX_UV] = surfaces[i].uvs; array[Mesh::ARRAY_TEX_UV] = surfaces[i].uvs;
if (have_tangents) {
array[Mesh::ARRAY_TANGENT] = surfaces[i].tans;
}
int idx = root_mesh->get_surface_count(); int idx = root_mesh->get_surface_count();
root_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, array); root_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, array);
@ -400,6 +507,15 @@ CSGShape::Operation CSGShape::get_operation() const {
return operation; return operation;
} }
void CSGShape::set_calculate_tangents(bool p_calculate_tangents) {
calculate_tangents = p_calculate_tangents;
_make_dirty();
}
bool CSGShape::is_calculating_tangents() const {
return calculate_tangents;
}
void CSGShape::_validate_property(PropertyInfo &property) const { void CSGShape::_validate_property(PropertyInfo &property) const {
if (is_inside_tree() && property.name.begins_with("use_collision") && !is_root_shape()) { if (is_inside_tree() && property.name.begins_with("use_collision") && !is_root_shape()) {
//hide collision if not root //hide collision if not root
@ -421,9 +537,13 @@ void CSGShape::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_snap", "snap"), &CSGShape::set_snap); ClassDB::bind_method(D_METHOD("set_snap", "snap"), &CSGShape::set_snap);
ClassDB::bind_method(D_METHOD("get_snap"), &CSGShape::get_snap); ClassDB::bind_method(D_METHOD("get_snap"), &CSGShape::get_snap);
ClassDB::bind_method(D_METHOD("set_calculate_tangents", "enabled"), &CSGShape::set_calculate_tangents);
ClassDB::bind_method(D_METHOD("is_calculating_tangents"), &CSGShape::is_calculating_tangents);
ADD_PROPERTY(PropertyInfo(Variant::INT, "operation", PROPERTY_HINT_ENUM, "Union,Intersection,Subtraction"), "set_operation", "get_operation"); ADD_PROPERTY(PropertyInfo(Variant::INT, "operation", PROPERTY_HINT_ENUM, "Union,Intersection,Subtraction"), "set_operation", "get_operation");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_collision"), "set_use_collision", "is_using_collision"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_collision"), "set_use_collision", "is_using_collision");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "snap", PROPERTY_HINT_RANGE, "0.0001,1,0.001"), "set_snap", "get_snap"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "snap", PROPERTY_HINT_RANGE, "0.0001,1,0.001"), "set_snap", "get_snap");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "calculate_tangents"), "set_calculate_tangents", "is_calculating_tangents");
BIND_ENUM_CONSTANT(OPERATION_UNION); BIND_ENUM_CONSTANT(OPERATION_UNION);
BIND_ENUM_CONSTANT(OPERATION_INTERSECTION); BIND_ENUM_CONSTANT(OPERATION_INTERSECTION);
@ -438,6 +558,7 @@ CSGShape::CSGShape() {
use_collision = false; use_collision = false;
operation = OPERATION_UNION; operation = OPERATION_UNION;
snap = 0.001; snap = 0.001;
calculate_tangents = true;
} }
CSGShape::~CSGShape() { CSGShape::~CSGShape() {

View File

@ -36,6 +36,7 @@
#include "csg.h" #include "csg.h"
#include "scene/3d/visual_instance.h" #include "scene/3d/visual_instance.h"
#include "scene/resources/concave_polygon_shape.h" #include "scene/resources/concave_polygon_shape.h"
#include "thirdparty/misc/mikktspace.h"
class CSGShape : public VisualInstance { class CSGShape : public VisualInstance {
GDCLASS(CSGShape, VisualInstance); GDCLASS(CSGShape, VisualInstance);
@ -63,6 +64,8 @@ private:
Ref<ConcavePolygonShape> root_collision_shape; Ref<ConcavePolygonShape> root_collision_shape;
RID root_collision_instance; RID root_collision_instance;
bool calculate_tangents;
Ref<ArrayMesh> root_mesh; Ref<ArrayMesh> root_mesh;
struct Vector3Hasher { struct Vector3Hasher {
@ -78,14 +81,26 @@ private:
PoolVector<Vector3> vertices; PoolVector<Vector3> vertices;
PoolVector<Vector3> normals; PoolVector<Vector3> normals;
PoolVector<Vector2> uvs; PoolVector<Vector2> uvs;
PoolVector<float> tans;
Ref<Material> material; Ref<Material> material;
int last_added; int last_added;
PoolVector<Vector3>::Write verticesw; PoolVector<Vector3>::Write verticesw;
PoolVector<Vector3>::Write normalsw; PoolVector<Vector3>::Write normalsw;
PoolVector<Vector2>::Write uvsw; PoolVector<Vector2>::Write uvsw;
PoolVector<float>::Write tansw;
}; };
//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 mikktSetTSpaceBasic(const SMikkTSpaceContext *pContext, const float fvTangent[], const float fSign, 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);
void _update_shape(); void _update_shape();
protected: protected:
@ -115,6 +130,9 @@ public:
void set_snap(float p_snap); void set_snap(float p_snap);
float get_snap() const; float get_snap() const;
void set_calculate_tangents(bool p_calculate_tangents);
bool is_calculating_tangents() const;
bool is_root_shape() const; bool is_root_shape() const;
CSGShape(); CSGShape();
~CSGShape(); ~CSGShape();

View File

@ -20,6 +20,9 @@
</method> </method>
</methods> </methods>
<members> <members>
<member name="calculate_tangents" type="bool" setter="set_calculate_tangents" getter="is_calculating_tangents">
Calculate tangents for the CSG shape which allows the use of normal maps. This is only applied on the root shape, this setting is ignored on any child.
</member>
<member name="operation" type="int" setter="set_operation" getter="get_operation" enum="CSGShape.Operation"> <member name="operation" type="int" setter="set_operation" getter="get_operation" enum="CSGShape.Operation">
The operation that is performed on this shape. This is ignored for the first CSG child node as the operation is between this node and the previous child of this nodes parent. The operation that is performed on this shape. This is ignored for the first CSG child node as the operation is between this node and the previous child of this nodes parent.
</member> </member>