mirror of
https://github.com/panda3d/panda3d.git
synced 2025-09-30 16:58:40 -04:00
RM_tube
This commit is contained in:
parent
4d1615b740
commit
bacd4ad811
@ -29,8 +29,11 @@ CData() {
|
||||
_uv_mode = RopeNode::UV_none;
|
||||
_u_dominant = true;
|
||||
_uv_scale = 1.0f;
|
||||
_normal_mode = RopeNode::NM_none;
|
||||
_tube_up = LVector3f::up();
|
||||
_use_vertex_color = false;
|
||||
_num_subdiv = 10;
|
||||
_num_slices = 5;
|
||||
_thickness = 1.0f;
|
||||
}
|
||||
|
||||
@ -46,8 +49,11 @@ CData(const RopeNode::CData ©) :
|
||||
_uv_mode(copy._uv_mode),
|
||||
_u_dominant(copy._u_dominant),
|
||||
_uv_scale(copy._uv_scale),
|
||||
_normal_mode(copy._normal_mode),
|
||||
_tube_up(copy._tube_up),
|
||||
_use_vertex_color(copy._use_vertex_color),
|
||||
_num_subdiv(copy._num_subdiv),
|
||||
_num_slices(copy._num_slices),
|
||||
_thickness(copy._thickness)
|
||||
{
|
||||
}
|
||||
@ -149,23 +155,6 @@ get_uv_direction() const {
|
||||
return cdata->_u_dominant;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: set_uv_scale
|
||||
// Access: Published
|
||||
// Description: Specifies an additional scaling factor to apply to
|
||||
// generated UV's for the rope. This is a deprecated
|
||||
// interface; use set_uv_scale() that accepts a single
|
||||
// float, instead.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE void RopeNode::
|
||||
set_uv_scale(const LVecBase2f &uv_scale) {
|
||||
if (get_uv_direction()) {
|
||||
set_uv_scale(uv_scale[0]);
|
||||
} else {
|
||||
set_uv_scale(uv_scale[1]);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: set_uv_scale
|
||||
// Access: Published
|
||||
@ -192,6 +181,64 @@ get_uv_scale() const {
|
||||
return cdata->_uv_scale;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: set_normal_mode
|
||||
// Access: Published
|
||||
// Description: Specifies the kind of normals to generate for the
|
||||
// rope. This is only applicable when the RenderMode is
|
||||
// set to RM_tube; in the other render modes, normals
|
||||
// are never generated.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE void RopeNode::
|
||||
set_normal_mode(RopeNode::NormalMode normal_mode) {
|
||||
CDWriter cdata(_cycler);
|
||||
cdata->_normal_mode = normal_mode;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: get_normal_mode
|
||||
// Access: Published
|
||||
// Description: Returns the kind of normals to generate for the rope.
|
||||
// This is only applicable when the RenderMode is set to
|
||||
// RM_tube.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE RopeNode::NormalMode RopeNode::
|
||||
get_normal_mode() const {
|
||||
CDReader cdata(_cycler);
|
||||
return cdata->_normal_mode;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: set_tube_up
|
||||
// Access: Published
|
||||
// Description: Specifies a normal vector, generally perpendicular to
|
||||
// the main axis of the starting point of the curve,
|
||||
// that controls the "top" of the curve, when RenderMode
|
||||
// is RM_tube. This is used to orient the vertices that
|
||||
// make up the tube. If this vector is too nearly
|
||||
// parallel with the starting direction of the curve,
|
||||
// there may be a tendency for the whole tube to
|
||||
// gimble-lock around its primary axis.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE void RopeNode::
|
||||
set_tube_up(const LVector3f &tube_up) {
|
||||
CDWriter cdata(_cycler);
|
||||
cdata->_tube_up = tube_up;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: get_tube_up
|
||||
// Access: Published
|
||||
// Description: Returns the normal vector used to control the "top"
|
||||
// of the curve, when RenderMode is RM_tube. See
|
||||
// set_tube_up().
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE const LVector3f &RopeNode::
|
||||
get_tube_up() const {
|
||||
CDReader cdata(_cycler);
|
||||
return cdata->_tube_up;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: set_use_vertex_color
|
||||
// Access: Published
|
||||
@ -246,6 +293,39 @@ get_num_subdiv() const {
|
||||
return cdata->_num_subdiv;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: set_num_slices
|
||||
// Access: Published
|
||||
// Description: Specifies the number of radial subdivisions to make
|
||||
// if RenderMode is RM_tube. It is ignored in the other
|
||||
// render modes.
|
||||
//
|
||||
// Increasing this number increases the roundness of a
|
||||
// cross-section of the tube. The minimum value for a
|
||||
// dimensional tube is 3; setting it to 2 will get you a
|
||||
// thin piece of tape (which is similar to RM_billboard,
|
||||
// except it won't rotate to face the camera).
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE void RopeNode::
|
||||
set_num_slices(int num_slices) {
|
||||
nassertv(num_slices >= 0);
|
||||
CDWriter cdata(_cycler);
|
||||
cdata->_num_slices = num_slices;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: get_num_slices
|
||||
// Access: Published
|
||||
// Description: Returns the number of radial subdivisions to make if
|
||||
// RenderMode is RM_tube. It is ignored in the other
|
||||
// render modes. See set_num_slices().
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE int RopeNode::
|
||||
get_num_slices() const {
|
||||
CDReader cdata(_cycler);
|
||||
return cdata->_num_slices;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: set_thickness
|
||||
// Access: Published
|
||||
|
@ -165,6 +165,10 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) {
|
||||
case RM_billboard:
|
||||
render_billboard(trav, data, result);
|
||||
break;
|
||||
|
||||
case RM_tube:
|
||||
render_tube(trav, data, result);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -265,89 +269,45 @@ do_recompute_bound(const NodePath &rel_to) {
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void RopeNode::
|
||||
render_thread(CullTraverser *trav, CullTraverserData &data,
|
||||
NurbsCurveResult *result) {
|
||||
UVMode uv_mode = get_uv_mode();
|
||||
float uv_scale = get_uv_scale();
|
||||
bool u_dominant = get_uv_direction();
|
||||
bool use_vertex_color = get_use_vertex_color();
|
||||
NurbsCurveResult *result) const {
|
||||
CurveSegments curve_segments;
|
||||
get_connected_segments(curve_segments, result);
|
||||
|
||||
// Now we have stored one or more sequences of vertices down the
|
||||
// center strips. Go back through and calculate the vertices on
|
||||
// either side.
|
||||
|
||||
PTA_Vertexf verts;
|
||||
PTA_TexCoordf uvs;
|
||||
PTA_Colorf colors;
|
||||
|
||||
compute_thread_vertices(verts, uvs, colors, curve_segments);
|
||||
|
||||
// Finally, build the lengths array to make them into proper
|
||||
// line strips.
|
||||
|
||||
PTA_int lengths;
|
||||
int num_prims = 0;
|
||||
|
||||
int num_verts = get_num_subdiv() + 1;
|
||||
int num_segments = result->get_num_segments();
|
||||
float dist = 0.0f;
|
||||
for (int segment = 0; segment < num_segments; segment++) {
|
||||
LPoint3f last_point;
|
||||
for (int i = 0; i < num_verts; i++) {
|
||||
float t = (float)i / (float)(num_verts - 1);
|
||||
LPoint3f point;
|
||||
result->eval_segment_point(segment, t, point);
|
||||
verts.push_back(point);
|
||||
CurveSegments::const_iterator si;
|
||||
for (si = curve_segments.begin(); si != curve_segments.end(); ++si) {
|
||||
const CurveSegment &segment = (*si);
|
||||
|
||||
if (use_vertex_color) {
|
||||
Colorf color;
|
||||
result->eval_segment_extended_points(segment, t, 0, &color[0], 4),
|
||||
colors.push_back(color);
|
||||
}
|
||||
|
||||
t = result->get_segment_t(segment, t);
|
||||
switch (uv_mode) {
|
||||
case UV_none:
|
||||
break;
|
||||
|
||||
case UV_parametric:
|
||||
if (u_dominant) {
|
||||
uvs.push_back(TexCoordf(t * uv_scale, 0.0f));
|
||||
} else {
|
||||
uvs.push_back(TexCoordf(0.0f, t * uv_scale));
|
||||
}
|
||||
break;
|
||||
|
||||
case UV_distance:
|
||||
if (i != 0) {
|
||||
LVector3f vec = point - last_point;
|
||||
dist += vec.length();
|
||||
}
|
||||
if (u_dominant) {
|
||||
uvs.push_back(TexCoordf(dist * uv_scale, 0.0f));
|
||||
} else {
|
||||
uvs.push_back(TexCoordf(0.0f, dist * uv_scale));
|
||||
}
|
||||
break;
|
||||
|
||||
case UV_distance2:
|
||||
if (i != 0) {
|
||||
LVector3f vec = point - last_point;
|
||||
dist += vec.length_squared();
|
||||
}
|
||||
if (u_dominant) {
|
||||
uvs.push_back(TexCoordf(dist * uv_scale, 0.0f));
|
||||
} else {
|
||||
uvs.push_back(TexCoordf(0.0f, dist * uv_scale));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
last_point = point;
|
||||
}
|
||||
lengths.push_back(num_verts);
|
||||
lengths.push_back(segment.size());
|
||||
num_prims++;
|
||||
}
|
||||
|
||||
PT(GeomLinestrip) geom = new GeomLinestrip;
|
||||
geom->set_width(get_thickness());
|
||||
geom->set_num_prims(num_segments);
|
||||
geom->set_num_prims(num_prims);
|
||||
geom->set_coords(verts);
|
||||
if (uv_mode != UV_none) {
|
||||
if (get_uv_mode() != UV_none) {
|
||||
geom->set_texcoords(uvs, G_PER_VERTEX);
|
||||
}
|
||||
|
||||
if (use_vertex_color) {
|
||||
if (get_use_vertex_color()) {
|
||||
geom->set_colors(colors, G_PER_VERTEX);
|
||||
} else {
|
||||
colors.push_back(Colorf(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
geom->set_colors(colors, G_OVERALL);
|
||||
}
|
||||
geom->set_lengths(lengths);
|
||||
@ -365,11 +325,11 @@ render_thread(CullTraverser *trav, CullTraverserData &data,
|
||||
// perpendicular to the camera plane.
|
||||
//
|
||||
// In this mode, thickness is in spatial units, and
|
||||
// determines the with of the triangle strips.
|
||||
// determines the width of the triangle strips.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void RopeNode::
|
||||
render_billboard(CullTraverser *trav, CullTraverserData &data,
|
||||
NurbsCurveResult *result) {
|
||||
NurbsCurveResult *result) const {
|
||||
const TransformState *net_transform = data._net_transform;
|
||||
const TransformState *camera_transform = trav->get_camera_transform();
|
||||
|
||||
@ -377,142 +337,48 @@ render_billboard(CullTraverser *trav, CullTraverserData &data,
|
||||
net_transform->invert_compose(camera_transform);
|
||||
LVector3f camera_vec = LVector3f::forward() * rel_transform->get_mat();
|
||||
|
||||
float thickness = get_thickness();
|
||||
float radius = thickness * 0.5f;
|
||||
UVMode uv_mode = get_uv_mode();
|
||||
bool u_dominant = get_uv_direction();
|
||||
float uv_scale = get_uv_scale();
|
||||
|
||||
// We can't just build one tristrip per segment. Instead, we should
|
||||
// build one continuous tristrip for all connected segments, so we
|
||||
// can stitch them together properly at the seams.
|
||||
|
||||
int num_verts = get_num_subdiv() + 1;
|
||||
int num_segments = result->get_num_segments();
|
||||
|
||||
vector_Vertexf center_verts;
|
||||
vector_int center_lengths;
|
||||
vector_float center_t;
|
||||
|
||||
LPoint3f point;
|
||||
int cur_length = 0;
|
||||
for (int segment = 0; segment < num_segments; segment++) {
|
||||
LPoint3f first_point;
|
||||
result->eval_segment_point(segment, 0.0f, first_point);
|
||||
if (cur_length == 0 || point != first_point) {
|
||||
// If the first point of this segment is different from the last
|
||||
// point of the previous segment, end the tristrip and store the
|
||||
// point.
|
||||
if (cur_length != 0) {
|
||||
center_lengths.push_back(cur_length);
|
||||
}
|
||||
center_verts.push_back(first_point);
|
||||
center_t.push_back(result->get_segment_t(segment, 0.0f));
|
||||
cur_length = 1;
|
||||
}
|
||||
|
||||
// Store all the remaining points in this segment.
|
||||
for (int i = 1; i < num_verts; i++) {
|
||||
float t = (float)i / (float)(num_verts - 1);
|
||||
result->eval_segment_point(segment, t, point);
|
||||
center_verts.push_back(point);
|
||||
center_t.push_back(result->get_segment_t(segment, t));
|
||||
cur_length++;
|
||||
}
|
||||
}
|
||||
if (cur_length != 0) {
|
||||
center_lengths.push_back(cur_length);
|
||||
}
|
||||
CurveSegments curve_segments;
|
||||
get_connected_segments(curve_segments, result);
|
||||
|
||||
// Now we have stored one or more sequences of vertices down the
|
||||
// center strips. Go back and convert them into actual tristrips.
|
||||
// center strips. Go back through and calculate the vertices on
|
||||
// either side.
|
||||
|
||||
PTA_Vertexf verts;
|
||||
PTA_TexCoordf uvs;
|
||||
PTA_Colorf colors;
|
||||
|
||||
compute_billboard_vertices(verts, uvs, colors, camera_vec,
|
||||
curve_segments, result);
|
||||
|
||||
// Finally, build the lengths array to make them into proper
|
||||
// triangle strips. We don't need a vindex array here, since the
|
||||
// vertices just happened to end up in tristrip order.
|
||||
|
||||
PTA_int lengths;
|
||||
|
||||
int vi = 0;
|
||||
int num_prims = 0;
|
||||
float dist = 0.0f;
|
||||
for (int i = 0; i < (int)center_lengths.size(); i++) {
|
||||
int length = center_lengths[i];
|
||||
for (int j = 0; j < length; j++) {
|
||||
const Vertexf &point = center_verts[vi + j];
|
||||
float t = center_t[vi + j];
|
||||
LVector3f tangent;
|
||||
// Rather than evaluating the curve for the tangent, we derive
|
||||
// it from the neighboring points. This gives us better
|
||||
// behavior at the endpoints, where the tangent might go to
|
||||
// zero.
|
||||
if (j == 0) {
|
||||
tangent = center_verts[vi + j + 1] - point;
|
||||
} else if (j == length - 1) {
|
||||
tangent = point - center_verts[vi + j - 1];
|
||||
} else {
|
||||
tangent = center_verts[vi + j + 1] - center_verts[vi + j - 1];
|
||||
}
|
||||
LVector3f cross = normalize(tangent.cross(camera_vec));
|
||||
cross *= radius;
|
||||
verts.push_back(point + cross);
|
||||
verts.push_back(point - cross);
|
||||
switch (uv_mode) {
|
||||
case UV_none:
|
||||
break;
|
||||
|
||||
case UV_parametric:
|
||||
if (u_dominant) {
|
||||
uvs.push_back(TexCoordf(t * uv_scale, 1.0f));
|
||||
uvs.push_back(TexCoordf(t * uv_scale, 0.0f));
|
||||
} else {
|
||||
uvs.push_back(TexCoordf(1.0f, t * uv_scale));
|
||||
uvs.push_back(TexCoordf(0.0f, t * uv_scale));
|
||||
}
|
||||
break;
|
||||
CurveSegments::const_iterator si;
|
||||
for (si = curve_segments.begin(); si != curve_segments.end(); ++si) {
|
||||
const CurveSegment &segment = (*si);
|
||||
|
||||
case UV_distance:
|
||||
if (j != 0) {
|
||||
LVector3f vec = point - center_verts[vi + j - 1];
|
||||
dist += vec.length();
|
||||
}
|
||||
if (u_dominant) {
|
||||
uvs.push_back(TexCoordf(dist * uv_scale, 1.0f));
|
||||
uvs.push_back(TexCoordf(dist * uv_scale, 0.0f));
|
||||
} else {
|
||||
uvs.push_back(TexCoordf(1.0f, dist * uv_scale));
|
||||
uvs.push_back(TexCoordf(0.0f, dist * uv_scale));
|
||||
}
|
||||
break;
|
||||
|
||||
case UV_distance2:
|
||||
if (j != 0) {
|
||||
LVector3f vec = point - center_verts[vi + j - 1];
|
||||
dist += vec.length_squared();
|
||||
}
|
||||
if (u_dominant) {
|
||||
uvs.push_back(TexCoordf(dist * uv_scale, 1.0f));
|
||||
uvs.push_back(TexCoordf(dist * uv_scale, 0.0f));
|
||||
} else {
|
||||
uvs.push_back(TexCoordf(1.0f, dist * uv_scale));
|
||||
uvs.push_back(TexCoordf(0.0f, dist * uv_scale));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
vi += length;
|
||||
lengths.push_back(length * 2);
|
||||
lengths.push_back(segment.size() * 2);
|
||||
num_prims++;
|
||||
}
|
||||
|
||||
colors.push_back(Colorf(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
// And create a Geom for the rendering.
|
||||
|
||||
PT(Geom) geom = new GeomTristrip;
|
||||
geom->set_num_prims(num_prims);
|
||||
geom->set_coords(verts);
|
||||
if (uv_mode != UV_none) {
|
||||
if (get_uv_mode() != UV_none) {
|
||||
geom->set_texcoords(uvs, G_PER_VERTEX);
|
||||
}
|
||||
geom->set_colors(colors, G_OVERALL);
|
||||
if (get_use_vertex_color()) {
|
||||
geom->set_colors(colors, G_PER_VERTEX);
|
||||
} else {
|
||||
geom->set_colors(colors, G_OVERALL);
|
||||
}
|
||||
geom->set_lengths(lengths);
|
||||
|
||||
CullableObject *object = new CullableObject(geom, data._state,
|
||||
@ -520,6 +386,399 @@ render_billboard(CullTraverser *trav, CullTraverserData &data,
|
||||
trav->get_cull_handler()->record_object(object);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: RopeNode::render_tube
|
||||
// Access: Private
|
||||
// Description: Draws the rope in RM_tube mode. This draws a hollow
|
||||
// tube centered around the string.
|
||||
//
|
||||
// In this mode, thickness is in spatial units, and
|
||||
// determines the diameter of the tube.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void RopeNode::
|
||||
render_tube(CullTraverser *trav, CullTraverserData &data,
|
||||
NurbsCurveResult *result) const {
|
||||
CurveSegments curve_segments;
|
||||
get_connected_segments(curve_segments, result);
|
||||
|
||||
// Now, we build up a table of vertices, in a series of rings
|
||||
// around the circumference of the tube.
|
||||
|
||||
int num_slices = get_num_slices();
|
||||
int num_verts_per_slice;
|
||||
|
||||
PTA_Vertexf verts;
|
||||
PTA_Normalf normals;
|
||||
PTA_TexCoordf uvs;
|
||||
PTA_Colorf colors;
|
||||
|
||||
compute_tube_vertices(verts, normals, uvs, colors,
|
||||
num_verts_per_slice, curve_segments, result);
|
||||
|
||||
// Finally, go back one more time and build up the vindex array, to
|
||||
// tie all the triangle strips together.
|
||||
|
||||
PTA_ushort vindex;
|
||||
PTA_int lengths;
|
||||
|
||||
int num_prims = 0;
|
||||
int vi = 0;
|
||||
CurveSegments::const_iterator si;
|
||||
for (si = curve_segments.begin(); si != curve_segments.end(); ++si) {
|
||||
const CurveSegment &segment = (*si);
|
||||
|
||||
for (int s = 0; s < num_slices; ++s) {
|
||||
int s1 = (s + 1) % num_verts_per_slice;
|
||||
|
||||
for (size_t j = 0; j < segment.size(); ++j) {
|
||||
vindex.push_back((vi + j) * num_verts_per_slice + s);
|
||||
vindex.push_back((vi + j) * num_verts_per_slice + s1);
|
||||
}
|
||||
|
||||
lengths.push_back(segment.size() * 2);
|
||||
num_prims++;
|
||||
}
|
||||
vi += (int)segment.size();
|
||||
}
|
||||
|
||||
// And create a Geom for the rendering.
|
||||
|
||||
PT(Geom) geom = new GeomTristrip;
|
||||
geom->set_num_prims(num_prims);
|
||||
geom->set_coords(verts, vindex);
|
||||
if (get_uv_mode() != UV_none) {
|
||||
geom->set_texcoords(uvs, G_PER_VERTEX, vindex);
|
||||
}
|
||||
|
||||
if (get_normal_mode() == NM_vertex) {
|
||||
geom->set_normals(normals, G_PER_VERTEX, vindex);
|
||||
}
|
||||
|
||||
if (get_use_vertex_color()) {
|
||||
geom->set_colors(colors, G_PER_VERTEX, vindex);
|
||||
} else {
|
||||
geom->set_colors(colors, G_OVERALL);
|
||||
}
|
||||
geom->set_lengths(lengths);
|
||||
|
||||
CullableObject *object = new CullableObject(geom, data._state,
|
||||
data._render_transform);
|
||||
trav->get_cull_handler()->record_object(object);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: RopeNode::get_connected_segments
|
||||
// Access: Private
|
||||
// Description: Evaluates the string of vertices along the curve, and
|
||||
// also breaks them up into connected segments.
|
||||
//
|
||||
// Since the NurbsCurveEvaluator describes the curve as
|
||||
// a sequence of possibly-connected piecewise continuous
|
||||
// segments, this means joining together some adjacent
|
||||
// segments from the NurbsCurveEvaluator into a single
|
||||
// CurveSegment, if they happen to be connected (as most
|
||||
// will be).
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void RopeNode::
|
||||
get_connected_segments(RopeNode::CurveSegments &curve_segments,
|
||||
const NurbsCurveResult *result) const {
|
||||
int num_verts = get_num_subdiv() + 1;
|
||||
int num_segments = result->get_num_segments();
|
||||
bool use_vertex_color = get_use_vertex_color();
|
||||
|
||||
CurveSegment *curve_segment = NULL;
|
||||
LPoint3f last_point;
|
||||
|
||||
for (int segment = 0; segment < num_segments; ++segment) {
|
||||
LPoint3f point;
|
||||
result->eval_segment_point(segment, 0.0f, point);
|
||||
|
||||
if (curve_segment == (CurveSegment *)NULL ||
|
||||
!point.almost_equal(last_point)) {
|
||||
// If the first point of this segment is different from the last
|
||||
// point of the previous segment, end the previous segment and
|
||||
// begin a new one.
|
||||
curve_segments.push_back(CurveSegment());
|
||||
curve_segment = &curve_segments.back();
|
||||
|
||||
CurveVertex vtx;
|
||||
vtx._p = point;
|
||||
vtx._t = result->get_segment_t(segment, 0.0f);
|
||||
if (use_vertex_color) {
|
||||
result->eval_segment_extended_points(segment, 0.0f, 0, &vtx._c[0], 4);
|
||||
}
|
||||
|
||||
curve_segment->push_back(vtx);
|
||||
}
|
||||
|
||||
// Store all the remaining points in this segment.
|
||||
for (int i = 1; i < num_verts; ++i) {
|
||||
float t = (float)i / (float)(num_verts - 1);
|
||||
|
||||
CurveVertex vtx;
|
||||
result->eval_segment_point(segment, t, vtx._p);
|
||||
vtx._t = result->get_segment_t(segment, t);
|
||||
if (use_vertex_color) {
|
||||
result->eval_segment_extended_points(segment, t, 0, &vtx._c[0], 4);
|
||||
}
|
||||
|
||||
curve_segment->push_back(vtx);
|
||||
|
||||
last_point = vtx._p;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: RopeNode::compute_thread_vertices
|
||||
// Access: Private
|
||||
// Description: Calculates the vertices for a RM_thread render. This
|
||||
// just copies the vertices more-or-less directly into
|
||||
// the array.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void RopeNode::
|
||||
compute_thread_vertices(PTA_Vertexf &verts, PTA_TexCoordf &uvs,
|
||||
PTA_Colorf &colors,
|
||||
const RopeNode::CurveSegments &curve_segments) const {
|
||||
UVMode uv_mode = get_uv_mode();
|
||||
float uv_scale = get_uv_scale();
|
||||
bool u_dominant = get_uv_direction();
|
||||
bool use_vertex_color = get_use_vertex_color();
|
||||
|
||||
float dist = 0.0f;
|
||||
CurveSegments::const_iterator si;
|
||||
for (si = curve_segments.begin(); si != curve_segments.end(); ++si) {
|
||||
const CurveSegment &segment = (*si);
|
||||
for (size_t j = 0; j < segment.size(); ++j) {
|
||||
verts.push_back(segment[j]._p);
|
||||
|
||||
if (use_vertex_color) {
|
||||
colors.push_back(segment[j]._c);
|
||||
}
|
||||
|
||||
float uv_t = compute_uv_t(dist, uv_mode, uv_scale, segment, j);
|
||||
|
||||
if (uv_mode != UV_none) {
|
||||
if (u_dominant) {
|
||||
uvs.push_back(TexCoordf(uv_t, 0.0f));
|
||||
} else {
|
||||
uvs.push_back(TexCoordf(0.0f, uv_t));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!use_vertex_color) {
|
||||
colors.push_back(Colorf(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: RopeNode::compute_billboard_vertices
|
||||
// Access: Private
|
||||
// Description: Calculates the vertices for a RM_billboard render. This
|
||||
// puts a pair of vertices on either side of each
|
||||
// computed point in curve_segments.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void RopeNode::
|
||||
compute_billboard_vertices(PTA_Vertexf &verts, PTA_TexCoordf &uvs,
|
||||
PTA_Colorf &colors, const LVector3f &camera_vec,
|
||||
const RopeNode::CurveSegments &curve_segments,
|
||||
NurbsCurveResult *result) const {
|
||||
float thickness = get_thickness();
|
||||
float radius = thickness * 0.5f;
|
||||
UVMode uv_mode = get_uv_mode();
|
||||
float uv_scale = get_uv_scale();
|
||||
bool u_dominant = get_uv_direction();
|
||||
bool use_vertex_color = get_use_vertex_color();
|
||||
|
||||
float dist = 0.0f;
|
||||
CurveSegments::const_iterator si;
|
||||
for (si = curve_segments.begin(); si != curve_segments.end(); ++si) {
|
||||
const CurveSegment &segment = (*si);
|
||||
for (size_t j = 0; j < segment.size(); ++j) {
|
||||
LVector3f tangent;
|
||||
compute_tangent(tangent, segment, j, result);
|
||||
|
||||
LVector3f normal = cross(tangent, camera_vec);
|
||||
normal.normalize();
|
||||
|
||||
verts.push_back(segment[j]._p + normal * radius);
|
||||
verts.push_back(segment[j]._p - normal * radius);
|
||||
|
||||
if (use_vertex_color) {
|
||||
colors.push_back(segment[j]._c);
|
||||
colors.push_back(segment[j]._c);
|
||||
}
|
||||
|
||||
float uv_t = compute_uv_t(dist, uv_mode, uv_scale, segment, j);
|
||||
|
||||
if (uv_mode != UV_none) {
|
||||
if (u_dominant) {
|
||||
uvs.push_back(TexCoordf(uv_t, 1.0f));
|
||||
uvs.push_back(TexCoordf(uv_t, 0.0f));
|
||||
} else {
|
||||
uvs.push_back(TexCoordf(1.0f, uv_t));
|
||||
uvs.push_back(TexCoordf(0.0f, uv_t));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!use_vertex_color) {
|
||||
colors.push_back(Colorf(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: RopeNode::compute_tube_vertices
|
||||
// Access: Private
|
||||
// Description: Calculates the vertices for a RM_tube render. This
|
||||
// puts a ring of vertices around each computed point in
|
||||
// curve_segments.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void RopeNode::
|
||||
compute_tube_vertices(PTA_Vertexf &verts, PTA_Normalf &normals,
|
||||
PTA_TexCoordf &uvs, PTA_Colorf &colors,
|
||||
int &num_verts_per_slice,
|
||||
const RopeNode::CurveSegments &curve_segments,
|
||||
NurbsCurveResult *result) const {
|
||||
int num_slices = get_num_slices();
|
||||
num_verts_per_slice = num_slices;
|
||||
|
||||
float thickness = get_thickness();
|
||||
float radius = thickness * 0.5f;
|
||||
UVMode uv_mode = get_uv_mode();
|
||||
float uv_scale = get_uv_scale();
|
||||
bool u_dominant = get_uv_direction();
|
||||
NormalMode normal_mode = get_normal_mode();
|
||||
bool use_vertex_color = get_use_vertex_color();
|
||||
|
||||
// If we are generating UV's, we will need to duplicate the vertices
|
||||
// along the seam so that the UV's go through the whole range of
|
||||
// 0..1 instead of reflecting in the last polygon before the seam.
|
||||
if (uv_mode != UV_none) {
|
||||
++num_verts_per_slice;
|
||||
}
|
||||
|
||||
LVector3f up = get_tube_up();
|
||||
|
||||
float dist = 0.0f;
|
||||
CurveSegments::const_iterator si;
|
||||
for (si = curve_segments.begin(); si != curve_segments.end(); ++si) {
|
||||
const CurveSegment &segment = (*si);
|
||||
for (size_t j = 0; j < segment.size(); ++j) {
|
||||
LVector3f tangent;
|
||||
compute_tangent(tangent, segment, j, result);
|
||||
|
||||
LVector3f normal = cross(tangent, up);
|
||||
normal.normalize();
|
||||
up = cross(normal, tangent);
|
||||
|
||||
LMatrix3f rotate = LMatrix3f::rotate_mat(360.0f / (float)num_slices,
|
||||
tangent);
|
||||
|
||||
float uv_t = compute_uv_t(dist, uv_mode, uv_scale, segment, j);
|
||||
|
||||
for (int s = 0; s < num_verts_per_slice; ++s) {
|
||||
verts.push_back(segment[j]._p + normal * radius);
|
||||
|
||||
if (normal_mode == NM_vertex) {
|
||||
normals.push_back(normal);
|
||||
}
|
||||
|
||||
if (use_vertex_color) {
|
||||
colors.push_back(segment[j]._c);
|
||||
}
|
||||
|
||||
normal = normal * rotate;
|
||||
|
||||
if (uv_mode != UV_none) {
|
||||
float uv_s = (float)s / (float)num_slices;
|
||||
if (u_dominant) {
|
||||
uvs.push_back(TexCoordf(uv_t, uv_s));
|
||||
} else {
|
||||
uvs.push_back(TexCoordf(uv_s, uv_t));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!use_vertex_color) {
|
||||
colors.push_back(Colorf(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: RopeNode::compute_tangent
|
||||
// Access: Private, Static
|
||||
// Description: Computes the tangent to the curve at the indicated
|
||||
// point in the segment.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void RopeNode::
|
||||
compute_tangent(LVector3f &tangent, const RopeNode::CurveSegment &segment,
|
||||
size_t j, NurbsCurveResult *result) {
|
||||
// First, try to evaluate the tangent at the curve. This gives
|
||||
// better results at the ends at the endpoints where the tangent
|
||||
// does not go to zero.
|
||||
|
||||
/*
|
||||
Actually, on second thought this looks terrible.
|
||||
|
||||
if (result->eval_tangent(segment[j]._t, tangent)) {
|
||||
if (!tangent.almost_equal(LVector3f::zero())) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// If that failed (or produced a zero tangent), then derive the
|
||||
// tangent from the neighboring points instead.
|
||||
if (j == 0) {
|
||||
tangent = segment[j + 1]._p - segment[j]._p;
|
||||
} else if (j == segment.size() - 1) {
|
||||
tangent = segment[j]._p - segment[j - 1]._p;
|
||||
} else {
|
||||
tangent = segment[j + 1]._p - segment[j - 1]._p;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: RopeNode::compute_uv_t
|
||||
// Access: Private, Static
|
||||
// Description: Computes the texture coordinate along the curve for
|
||||
// the indicated point in the segment.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
float RopeNode::
|
||||
compute_uv_t(float &dist, const RopeNode::UVMode &uv_mode,
|
||||
float uv_scale, const RopeNode::CurveSegment &segment,
|
||||
size_t j) {
|
||||
switch (uv_mode) {
|
||||
case UV_none:
|
||||
return 0.0f;
|
||||
|
||||
case UV_parametric:
|
||||
return segment[j]._t * uv_scale;
|
||||
|
||||
case UV_distance:
|
||||
if (j != 0) {
|
||||
LVector3f vec = segment[j]._p - segment[j - 1]._p;
|
||||
dist += vec.length();
|
||||
}
|
||||
return dist * uv_scale;
|
||||
|
||||
case UV_distance2:
|
||||
if (j != 0) {
|
||||
LVector3f vec = segment[j]._p - segment[j - 1]._p;
|
||||
dist += vec.length_squared();
|
||||
}
|
||||
return dist * uv_scale;
|
||||
}
|
||||
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: RopeNode::register_with_read_factory
|
||||
// Access: Public, Static
|
||||
|
@ -59,7 +59,10 @@ PUBLISHED:
|
||||
|
||||
// Render the rope as a continuous triangle strip oriented to be
|
||||
// perpendicular to the view vector.
|
||||
RM_billboard
|
||||
RM_billboard,
|
||||
|
||||
// Render the rope as a hollow tube extruded along its length.
|
||||
RM_tube
|
||||
};
|
||||
|
||||
enum UVMode {
|
||||
@ -83,6 +86,14 @@ PUBLISHED:
|
||||
UV_distance2,
|
||||
};
|
||||
|
||||
enum NormalMode {
|
||||
// Don't generate normals.
|
||||
NM_none,
|
||||
|
||||
// Generate vertex (smooth-shaded) normals.
|
||||
NM_vertex
|
||||
};
|
||||
|
||||
INLINE void set_curve(NurbsCurveEvaluator *curve);
|
||||
INLINE NurbsCurveEvaluator *get_curve() const;
|
||||
|
||||
@ -95,16 +106,24 @@ PUBLISHED:
|
||||
INLINE void set_uv_direction(bool u_dominant);
|
||||
INLINE bool get_uv_direction() const;
|
||||
|
||||
INLINE void set_uv_scale(const LVecBase2f &uv_scale);
|
||||
INLINE void set_uv_scale(float scale);
|
||||
INLINE float get_uv_scale() const;
|
||||
|
||||
INLINE void set_normal_mode(NormalMode normal_mode);
|
||||
INLINE NormalMode get_normal_mode() const;
|
||||
|
||||
INLINE void set_tube_up(const LVector3f &tube_up);
|
||||
INLINE const LVector3f &get_tube_up() const;
|
||||
|
||||
INLINE void set_use_vertex_color(bool flag);
|
||||
INLINE bool get_use_vertex_color() const;
|
||||
|
||||
INLINE void set_num_subdiv(int num_subdiv);
|
||||
INLINE int get_num_subdiv() const;
|
||||
|
||||
INLINE void set_num_slices(int num_slices);
|
||||
INLINE int get_num_slices() const;
|
||||
|
||||
INLINE void set_thickness(float thickness);
|
||||
INLINE float get_thickness() const;
|
||||
|
||||
@ -116,9 +135,44 @@ protected:
|
||||
private:
|
||||
BoundingVolume *do_recompute_bound(const NodePath &rel_to);
|
||||
void render_thread(CullTraverser *trav, CullTraverserData &data,
|
||||
NurbsCurveResult *result);
|
||||
NurbsCurveResult *result) const;
|
||||
void render_billboard(CullTraverser *trav, CullTraverserData &data,
|
||||
NurbsCurveResult *result);
|
||||
NurbsCurveResult *result) const;
|
||||
void render_tube(CullTraverser *trav, CullTraverserData &data,
|
||||
NurbsCurveResult *result) const;
|
||||
|
||||
class CurveVertex {
|
||||
public:
|
||||
LPoint3f _p;
|
||||
Colorf _c;
|
||||
float _t;
|
||||
};
|
||||
typedef pvector<CurveVertex> CurveSegment;
|
||||
typedef pvector<CurveSegment> CurveSegments;
|
||||
|
||||
void get_connected_segments(CurveSegments &curve_segments,
|
||||
const NurbsCurveResult *result) const;
|
||||
|
||||
void compute_thread_vertices(PTA_Vertexf &verts, PTA_TexCoordf &uvs,
|
||||
PTA_Colorf &colors,
|
||||
const CurveSegments &curve_segments) const;
|
||||
void compute_billboard_vertices(PTA_Vertexf &verts, PTA_TexCoordf &uvs,
|
||||
PTA_Colorf &colors,
|
||||
const LVector3f &camera_vec,
|
||||
const CurveSegments &curve_segments,
|
||||
NurbsCurveResult *result) const;
|
||||
void compute_tube_vertices(PTA_Vertexf &verts, PTA_Normalf &normals,
|
||||
PTA_TexCoordf &uvs, PTA_Colorf &colors,
|
||||
int &num_verts_per_slice,
|
||||
const CurveSegments &curve_segments,
|
||||
NurbsCurveResult *result) const;
|
||||
|
||||
static void compute_tangent(LVector3f &tangent, const CurveSegment &segment,
|
||||
size_t j, NurbsCurveResult *result);
|
||||
static float compute_uv_t(float &dist, const UVMode &uv_mode,
|
||||
float uv_scale, const CurveSegment &segment,
|
||||
size_t j);
|
||||
|
||||
|
||||
private:
|
||||
// This is the data that must be cycled between pipeline stages.
|
||||
@ -135,8 +189,11 @@ private:
|
||||
UVMode _uv_mode;
|
||||
bool _u_dominant;
|
||||
float _uv_scale;
|
||||
NormalMode _normal_mode;
|
||||
LVector3f _tube_up;
|
||||
bool _use_vertex_color;
|
||||
int _num_subdiv;
|
||||
int _num_slices;
|
||||
float _thickness;
|
||||
};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user