536 lines
16 KiB
C++
536 lines
16 KiB
C++
/**
|
|
* PANDA 3D SOFTWARE
|
|
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
|
*
|
|
* All use of this software is subject to the terms of the revised BSD
|
|
* license. You should have received a copy of this license along
|
|
* with this source code in a file named "LICENSE."
|
|
*
|
|
* @file meshDrawer.cxx
|
|
* @author treeform
|
|
* @date 2008-12-19
|
|
*/
|
|
|
|
#include "meshDrawer.h"
|
|
|
|
#include "geomVertexFormat.h"
|
|
#include "geomVertexArrayFormat.h"
|
|
#include "geomVertexData.h"
|
|
#include "geomVertexWriter.h"
|
|
#include "geomVertexRewriter.h"
|
|
#include "camera.h"
|
|
#include "boundingSphere.h"
|
|
#include "geomTristrips.h"
|
|
#include "geomTriangles.h"
|
|
#include "geom.h"
|
|
#include "geomNode.h"
|
|
#include "pnmPainter.h"
|
|
#include "pnmBrush.h"
|
|
#include "lvecBase4.h"
|
|
#include "lvector3.h"
|
|
#include "pandaNode.h"
|
|
|
|
TypeHandle MeshDrawer::_type_handle;
|
|
|
|
PN_stdfloat randFloat() {
|
|
return ((PN_stdfloat)rand() / (PN_stdfloat)RAND_MAX);
|
|
}
|
|
|
|
/**
|
|
* Creates a system with a given budget.
|
|
*/
|
|
void MeshDrawer::generator(int budget) {
|
|
// create enough triangles for budget:
|
|
_vdata = new GeomVertexData(_root.get_name(), GeomVertexFormat::get_v3n3c4t2(), Geom::UH_static);//UH_dynamic);
|
|
_vdata->unclean_set_num_rows(budget * 3);
|
|
|
|
{
|
|
GeomVertexWriter tvertex(_vdata, "vertex");
|
|
GeomVertexWriter tnormal(_vdata, "normal");
|
|
GeomVertexWriter tuv(_vdata, "texcoord");
|
|
GeomVertexWriter tcolor(_vdata, "color");
|
|
|
|
// iterate and fill _up a geom with random data so that it will not be
|
|
// optimized out by panda3d system
|
|
for (int i = 0; i < budget; i++) {
|
|
for (int vert = 0; vert < 3; vert++) {
|
|
LVector3 vec3 = LVector3(randFloat()+1000,randFloat(),randFloat())*.001;
|
|
LVector4 vec4 = LVector4(1,1,1,randFloat());
|
|
LVector2 vec2 = LVector2(0,randFloat());
|
|
tvertex.set_data3(vec3);
|
|
tcolor.set_data4(vec4);
|
|
tuv.set_data2(vec2);
|
|
tnormal.set_data3(vec3);
|
|
}
|
|
}
|
|
}
|
|
|
|
// create our node and attach it to this node path
|
|
_prim = new GeomTriangles(Geom::UH_static);
|
|
_prim->add_next_vertices(budget * 3);
|
|
_prim->close_primitive();
|
|
_geom = new Geom(_vdata);
|
|
_geom->add_primitive(_prim);
|
|
if (_geomnode == nullptr) {
|
|
_geomnode = new GeomNode("__MeshDrawer_GeomNode");
|
|
_root.attach_new_node(_geomnode);
|
|
} else {
|
|
_geomnode->remove_all_geoms();
|
|
}
|
|
_geomnode->add_geom(_geom);
|
|
_last_clear_index = budget;
|
|
}
|
|
|
|
/**
|
|
* Pass the current camera node and the root node. Passing the camera is
|
|
* required to generate bill boards that face it.
|
|
*/
|
|
void MeshDrawer::begin(NodePath camera, NodePath render) {
|
|
// sanity check
|
|
assert(render.get_error_type() == NodePath::ET_ok);
|
|
assert(camera.get_error_type() == NodePath::ET_ok);
|
|
|
|
// remember our arguments
|
|
_camera = camera;
|
|
_render = render;
|
|
|
|
// compute some help vectors
|
|
_eyePos = camera.get_pos();
|
|
_up = _render.get_relative_vector(camera, LVector3(0, 0, 1));
|
|
_right = _render.get_relative_vector(camera, LVector3(1, 0, 0));
|
|
_b1 = - _right - _up;
|
|
_b2 = _right - _up;
|
|
_b3 = _right + _up;
|
|
_b4 = - _right + _up;
|
|
|
|
// recreate our rewriters
|
|
if (_vertex != nullptr) delete _vertex;
|
|
if (_normal != nullptr) delete _normal;
|
|
if (_uv != nullptr) delete _uv;
|
|
if (_color != nullptr) delete _color;
|
|
|
|
if (_vdata == nullptr) {
|
|
generator(_budget);
|
|
}
|
|
|
|
_vertex = new GeomVertexRewriter(_vdata, "vertex");
|
|
_uv = new GeomVertexRewriter(_vdata, "texcoord");
|
|
_normal = new GeomVertexRewriter(_vdata, "normal");
|
|
_color = new GeomVertexRewriter(_vdata, "color");
|
|
_dprim = _prim->decompose();
|
|
|
|
// reseta our clearning indexes
|
|
_start_clear_index = 0;
|
|
_end_clear_index = _budget;
|
|
_clear_index = _start_clear_index;
|
|
|
|
}
|
|
|
|
/**
|
|
* Finish the drawing and clearing off the remaining vertexes.
|
|
*/
|
|
void MeshDrawer::end() {
|
|
|
|
// clear the unused triangles at the end of the buffer
|
|
for(int i = _clear_index ; i < _last_clear_index; i ++ ) {
|
|
_vertex->add_data3(0,0,0);
|
|
_vertex->add_data3(0,0,0);
|
|
_vertex->add_data3(0,0,0);
|
|
}
|
|
// don't clear more then you have too
|
|
_last_clear_index = _clear_index;
|
|
|
|
// delete the re writers
|
|
delete _vertex; _vertex = nullptr;
|
|
delete _uv; _uv = nullptr;
|
|
delete _normal; _normal = nullptr;
|
|
delete _color; _color = nullptr;
|
|
|
|
}
|
|
|
|
/**
|
|
* Draws a particle that is sort of like a bill board but has an extra
|
|
* rotation component. Frame contains u,v,u-size,v-size quadruple.
|
|
*/
|
|
void MeshDrawer::particle(const LVector3 &pos, const LVector4 &frame, PN_stdfloat size,
|
|
const LVector4 &color, PN_stdfloat rotation) {
|
|
|
|
rotation = rotation / 57.29578;
|
|
|
|
LVector3 v1 = pos + _b1*size*sin(rotation) + _b2*size*cos(rotation);
|
|
LVector3 v2 = pos + _b2*size*sin(rotation) + _b3*size*cos(rotation);
|
|
LVector3 v3 = pos + _b3*size*sin(rotation) + _b4*size*cos(rotation);
|
|
LVector3 v4 = pos + _b4*size*sin(rotation) + _b1*size*cos(rotation);
|
|
|
|
PN_stdfloat u = frame.get_x();
|
|
PN_stdfloat v = frame.get_y();
|
|
PN_stdfloat us = frame.get_z();
|
|
PN_stdfloat vs = frame.get_w();
|
|
|
|
tri(
|
|
v1, color, LVector2(u,v),
|
|
v2, color, LVector2(u+us,v),
|
|
v3, color, LVector2(u+us,v+vs));
|
|
tri(
|
|
v3, color, LVector2(u+us,v+vs),
|
|
v4, color, LVector2(u,v+vs),
|
|
v1, color, LVector2(u,v));
|
|
}
|
|
|
|
/**
|
|
* Works just like particle but accepts 2 frames and a blend (from 0 to 1)
|
|
* component between them Frame contains u,v,u-size,v-size quadruple.
|
|
*/
|
|
void MeshDrawer::blended_particle(const LVector3 &pos, const LVector4 &frame1,
|
|
const LVector4 &frame2, PN_stdfloat blend, PN_stdfloat size, const LVector4 &color, PN_stdfloat rotation) {
|
|
|
|
LVector4 c2 = color;
|
|
PN_stdfloat original_w = c2.get_w();
|
|
c2.set_w((1.f-blend)*original_w);
|
|
particle(pos,frame1,size,c2,rotation);
|
|
c2.set_w(blend*original_w);
|
|
particle(pos,frame2,size,c2,rotation);
|
|
|
|
}
|
|
|
|
/**
|
|
* Draws a billboard - particle with no rotation. Billboards always face the
|
|
* camera. Frame contains u,v,u-size,v-size quadruple.
|
|
*/
|
|
void MeshDrawer::billboard(const LVector3 &pos, const LVector4 &frame, PN_stdfloat size,
|
|
const LVector4 &_color) {
|
|
|
|
LVector3 v1 = pos + _b1*size;
|
|
LVector3 v2 = pos + _b2*size;
|
|
LVector3 v3 = pos + _b3*size;
|
|
LVector3 v4 = pos + _b4*size;
|
|
|
|
PN_stdfloat u = frame.get_x();
|
|
PN_stdfloat v = frame.get_y();
|
|
PN_stdfloat us = frame.get_z();
|
|
PN_stdfloat vs = frame.get_w();
|
|
|
|
tri(
|
|
v1, _color, LVector2(u,v),
|
|
v2, _color, LVector2(u+us,v),
|
|
v3, _color, LVector2(u+us,v+vs));
|
|
tri(
|
|
v3, _color, LVector2(u+us,v+vs),
|
|
v4, _color, LVector2(u,v+vs),
|
|
v1, _color, LVector2(u,v));
|
|
}
|
|
|
|
|
|
/**
|
|
* Draws a segment a line with a thickness. That has billboarding effect.
|
|
* Frame contains u,v,u-size,v-size quadruple.
|
|
*/
|
|
void MeshDrawer::segment(const LVector3 &start, const LVector3 &stop, const LVector4 &frame,
|
|
PN_stdfloat thickness, const LVector4 &color) {
|
|
link_segment(start, frame, thickness, color);
|
|
link_segment(stop, frame, thickness, color);
|
|
link_segment_end(frame, color);
|
|
}
|
|
/**
|
|
* Draws a segment a line with a thickness. This segment does not use the
|
|
* bill boarding behavior and instead draws 2 planes in a cross. Stars at
|
|
* start and ends at stop. Frame contains u,v,u-size,v-size quadruple.
|
|
*/
|
|
void MeshDrawer::cross_segment(const LVector3 &start, const LVector3 &stop, const LVector4 &frame,
|
|
PN_stdfloat thickness, const LVector4 &color) {
|
|
|
|
PN_stdfloat u = frame.get_x();
|
|
PN_stdfloat v = frame.get_y();
|
|
PN_stdfloat us = frame.get_z();
|
|
PN_stdfloat vs = frame.get_w();
|
|
|
|
LVector3 v1 = start - _up*thickness;
|
|
LVector3 v2 = stop - _up*thickness;
|
|
LVector3 v3 = stop + _up*thickness;
|
|
LVector3 v4 = start + _up*thickness;
|
|
|
|
tri(v1, color, LVector2(u,v),
|
|
v2, color, LVector2(u+us,v),
|
|
v3, color, LVector2(u+us,v+vs));
|
|
tri(v3, color, LVector2(u+us,v+vs),
|
|
v4, color, LVector2(u,v+vs),
|
|
v1, color, LVector2(u,v));
|
|
|
|
v1 = start - _right*thickness;
|
|
v2 = stop - _right*thickness;
|
|
v3 = stop + _right*thickness;
|
|
v4 = start + _right*thickness;
|
|
|
|
tri(v1, color, LVector2(u,v),
|
|
v2, color, LVector2(u+us,v),
|
|
v3, color, LVector2(u+us,v+vs));
|
|
tri(v3, color, LVector2(u+us,v+vs),
|
|
v4, color, LVector2(u,v+vs),
|
|
v1, color, LVector2(u,v));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
* Draws a segment a line with different thickness and color on both sides.
|
|
* Stars at start and ends at stop. Frame contains u,v,u-size,v-size
|
|
* quadruple.
|
|
*/
|
|
void MeshDrawer::uneven_segment(const LVector3 &start, const LVector3 &stop,
|
|
const LVector4 &frame, PN_stdfloat thickness_start, const LVector4 &color_start,
|
|
PN_stdfloat thickness_stop, const LVector4 &color_stop) {
|
|
|
|
PN_stdfloat u = frame.get_x();
|
|
PN_stdfloat v = frame.get_y();
|
|
PN_stdfloat us = frame.get_z();
|
|
PN_stdfloat vs = frame.get_w();
|
|
|
|
LVector3 v1 = start - _up*thickness_start;
|
|
LVector3 v2 = stop - _up*thickness_stop;
|
|
LVector3 v3 = stop + _up*thickness_stop;
|
|
LVector3 v4 = start + _up*thickness_start;
|
|
|
|
tri(v1, color_start, LVector2(u,v),
|
|
v2, color_stop, LVector2(u+us,v),
|
|
v3, color_stop, LVector2(u+us,v+vs));
|
|
tri(v3, color_stop, LVector2(u+us,v+vs),
|
|
v4, color_start, LVector2(u,v+vs),
|
|
v1, color_start, LVector2(u,v));
|
|
|
|
v1 = start - _right*thickness_start;
|
|
v2 = stop - _right*thickness_stop;
|
|
v3 = stop + _right*thickness_stop;
|
|
v4 = start + _right*thickness_start;
|
|
|
|
tri(v1, color_start, LVector2(u,v),
|
|
v2, color_stop, LVector2(u+us,v),
|
|
v3, color_stop, LVector2(u+us,v+vs));
|
|
tri(v3, color_stop, LVector2(u+us,v+vs),
|
|
v4, color_start, LVector2(u,v+vs),
|
|
v1, color_start, LVector2(u,v));
|
|
}
|
|
|
|
/**
|
|
* Draws number of particles in a sphere like emitter. Frame contains
|
|
* u,v,u-size,v-size quadruple.
|
|
*/
|
|
void MeshDrawer::explosion(
|
|
const LVector3 &pos, const LVector4 &frame, PN_stdfloat size, const LVector4 &_color,
|
|
int seed, int number, PN_stdfloat distance) {
|
|
srand(seed);
|
|
LVector3 relative_pos;
|
|
for(int i = 0; i < number; i++) {
|
|
relative_pos = LVector3(randFloat()-.5f,randFloat()-.5f,randFloat()-.5f);
|
|
relative_pos.normalize();
|
|
relative_pos *= randFloat()*distance;
|
|
particle(relative_pos+pos,frame,size,_color,randFloat()*360.0f);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Draws a number of particles in a big line with a shift dictated by the
|
|
* offset. Frame contains u,v,u-size,v-size quadruple.
|
|
*/
|
|
void MeshDrawer::stream(const LVector3 &start, const LVector3 &stop, const LVector4 &frame, PN_stdfloat size, const LVector4 &_color,
|
|
int number, PN_stdfloat offset) {
|
|
|
|
offset = offset-floor(offset);
|
|
LVector3 relative_pos = stop;
|
|
LVector3 vec = stop - start;
|
|
PN_stdfloat distance = vec.length();
|
|
vec.normalize();
|
|
for(int i = 0; i < number; i++) {
|
|
relative_pos = start + vec * ((i+offset)*(distance/PN_stdfloat(number)));
|
|
billboard(relative_pos,frame,size,_color);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Draws the geometry that is inside this node path into the MeshDrawer
|
|
* object. This performs a similar functions as RigidBodyCombiner but for
|
|
* very dynamic situations that share the same texture like physcal chunks of
|
|
* explosions. It can be a little slow
|
|
*/
|
|
void MeshDrawer::geometry(NodePath draw_node) {
|
|
assert(_render.get_error_type() == NodePath::ET_ok);
|
|
|
|
LVector4 color = LVector4(1,1,1,1);
|
|
LVector3 vec[3];
|
|
LVector2 uv[3];
|
|
|
|
// process node
|
|
NodePathCollection geom_collection = draw_node.find_all_matches("**/+GeomNode");
|
|
for(int i=0; i < geom_collection.get_num_paths(); i++ ) {
|
|
NodePath current_node_path = geom_collection.get_path(i);
|
|
PT(GeomNode) geomNode = DCAST(GeomNode, current_node_path.node());
|
|
|
|
// process geom node
|
|
for(int j=0; j<geomNode->get_num_geoms(); j++) {
|
|
CPT(Geom) geom = geomNode->get_geom(j);
|
|
CPT(GeomVertexData) v_data = geom->get_vertex_data();
|
|
GeomVertexReader *prim_vertex_reader = new GeomVertexReader(v_data, "vertex");
|
|
GeomVertexReader *prim_uv_reader = new GeomVertexReader(v_data, "texcoord");
|
|
for (size_t k = 0; k < geom->get_num_primitives(); ++k) {
|
|
CPT(GeomPrimitive) prim1 = geom->get_primitive(k);
|
|
CPT(GeomPrimitive) _prim = prim1->decompose();
|
|
|
|
// process primitive
|
|
for(int p=0; p < _prim->get_num_primitives();p++) {
|
|
int s = _prim->get_primitive_start(p);
|
|
int e = _prim->get_primitive_end(p);
|
|
int indx_over = 0;
|
|
|
|
// process polygon
|
|
for(int idx=s; idx<e; idx++) {
|
|
int vidx = _prim->get_vertex(idx);
|
|
prim_vertex_reader->set_row_unsafe(vidx);
|
|
prim_uv_reader->set_row_unsafe(vidx);
|
|
vec[indx_over] = _render.get_relative_point(
|
|
current_node_path,prim_vertex_reader->get_data3());
|
|
uv[indx_over] = prim_uv_reader->get_data2();
|
|
indx_over++;
|
|
if (indx_over > 2) break;
|
|
}
|
|
|
|
// draw polygon
|
|
tri(vec[0],color,uv[0],
|
|
vec[1],color,uv[1],
|
|
vec[2],color,uv[2]);
|
|
}
|
|
// if we are over budget just quit
|
|
if( _clear_index > _end_clear_index) return;
|
|
}
|
|
// delete our reders
|
|
delete prim_vertex_reader;
|
|
delete prim_uv_reader;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Stars or continues linked segment. Control position, frame, thickness and
|
|
* color with parameters. Frame contains u,v,u-size,v-size quadruple.
|
|
*/
|
|
void MeshDrawer::
|
|
link_segment(const LVector3 &pos, const LVector4 &frame,
|
|
PN_stdfloat thickness, const LVector4 &color) {
|
|
assert(_render.get_error_type() == NodePath::ET_ok);
|
|
assert(_camera.get_error_type() == NodePath::ET_ok);
|
|
/*
|
|
* X
|
|
* ---X
|
|
* ===0---X
|
|
* ===0===0---X
|
|
* ===0===0===O---X
|
|
* ===0===0===0===End
|
|
*
|
|
* first call marks position X
|
|
* second call moves position and promises to draw segment
|
|
* it can't draw it yet because next segment might bend it
|
|
* third call finally draws segment
|
|
* and the chain continues till
|
|
* link_segment_end to flush the linking segments is called.
|
|
*/
|
|
|
|
// mark 1st position
|
|
if(_at_start==0) {
|
|
_last_pos = pos;
|
|
_last_thickness = thickness;
|
|
_last_color = color;
|
|
_at_start=1;
|
|
return;
|
|
}
|
|
|
|
LVector3 start = _last_pos;
|
|
LVector3 stop = pos;
|
|
|
|
LVector3 cam_start3d = _camera.get_relative_point(_render, start);
|
|
LPoint2 cam_start2d = LVector2();
|
|
LVector3 cam_stop3d = _camera.get_relative_point(_render, stop);
|
|
LPoint2 cam_stop2d = LVector2();
|
|
|
|
const Camera *camera;
|
|
DCAST_INTO_V(camera, _camera.node());
|
|
const Lens *lens = camera->get_lens();
|
|
|
|
lens->project(cam_start3d, cam_start2d);
|
|
lens->project(cam_stop3d, cam_stop2d);
|
|
|
|
LVector2 dif = cam_stop2d - cam_start2d;
|
|
PN_stdfloat rotation = atan2(dif.get_x(),dif.get_y());
|
|
|
|
LVector3 now_v1 = start + _b1*(PN_stdfloat)(thickness*sin(rotation)) + _b2*(PN_stdfloat)(thickness*cos(rotation));
|
|
LVector3 now_v4 = start + _b4*(PN_stdfloat)(thickness*sin(rotation)) + _b1*(PN_stdfloat)(thickness*cos(rotation));
|
|
LVector3 now_v2 = stop + _b2*(PN_stdfloat)(thickness*sin(rotation)) + _b3*(PN_stdfloat)(thickness*cos(rotation));
|
|
LVector3 now_v3 = stop + _b3*(PN_stdfloat)(thickness*sin(rotation)) + _b4*(PN_stdfloat)(thickness*cos(rotation));
|
|
|
|
// mark the segment we going to draw we need to draw it when we know what
|
|
// the next segment looks like because it can bend it a little
|
|
if(_at_start==1) {
|
|
_last_v1 = now_v1;
|
|
_last_v2 = now_v2;
|
|
_last_v3 = now_v3;
|
|
_last_v4 = now_v4;
|
|
_at_start = 2;
|
|
return;
|
|
}
|
|
|
|
// draw the last segment a little bent
|
|
LVector3 v1 = _last_v1;
|
|
LVector3 v2 = (_last_v2+now_v1)/2.0f;
|
|
LVector3 v3 = (_last_v3+now_v4)/2.0f;
|
|
LVector3 v4 = _last_v4;
|
|
|
|
// compute this frame
|
|
PN_stdfloat u = frame.get_x();
|
|
PN_stdfloat v = frame.get_y();
|
|
PN_stdfloat us = frame.get_z();
|
|
PN_stdfloat vs = frame.get_w();
|
|
|
|
tri(v1, _last_color, LVector2(u,v),
|
|
v2, color, LVector2(u+us,v),
|
|
v3, color, LVector2(u+us,v+vs));
|
|
tri(v3, color, LVector2(u+us,v+vs),
|
|
v4, _last_color, LVector2(u,v+vs),
|
|
v1, _last_color, LVector2(u,v));
|
|
|
|
// save this segment
|
|
_last_v1 = v2;
|
|
_last_v2 = now_v2;
|
|
_last_v3 = now_v3;
|
|
_last_v4 = v3;
|
|
|
|
// make this position
|
|
_last_pos = pos;
|
|
_last_thickness = thickness;
|
|
_last_color = color;
|
|
}
|
|
|
|
/**
|
|
* Finish drawing linked segments, needs at least two calls to link_segment
|
|
* before it can end the linked segment. Frame contains u,v,u-size,v-size
|
|
* quadruple.
|
|
*/
|
|
void MeshDrawer::link_segment_end(const LVector4 &frame, const LVector4 &color)
|
|
{
|
|
PN_stdfloat u = frame.get_x();
|
|
PN_stdfloat v = frame.get_y();
|
|
PN_stdfloat us = frame.get_z();
|
|
PN_stdfloat vs = frame.get_w();
|
|
|
|
tri(_last_v1, _last_color, LVector2(u,v),
|
|
_last_v2, color, LVector2(u+us,v),
|
|
_last_v3, color, LVector2(u+us,v+vs));
|
|
tri(_last_v3, color, LVector2(u+us,v+vs),
|
|
_last_v4, _last_color, LVector2(u,v+vs),
|
|
_last_v1, _last_color, LVector2(u,v));
|
|
|
|
_at_start = 0;
|
|
}
|