/* Copyright (C) 2001-2006, William Joseph. All Rights Reserved. This file is part of GtkRadiant. GtkRadiant is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. GtkRadiant is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GtkRadiant; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #if !defined(INCLUDED_PATCH_H) #define INCLUDED_PATCH_H /// \file /// \brief The patch primitive. /// /// A 2-dimensional matrix of vertices that define a quadratic bezier surface. /// The Boundary-Representation of this primitive is a triangle mesh. /// The surface is recursively tesselated until the angle between each triangle /// edge is smaller than a specified tolerance. #include "nameable.h" #include "ifilter.h" #include "imap.h" #include "ipatch.h" #include "cullable.h" #include "renderable.h" #include "editable.h" #include "selectable.h" #include "debugging/debugging.h" #include #include #include "math/frustum.h" #include "string/string.h" #include "stream/stringstream.h" #include "stream/textstream.h" #include "xml/xmlelement.h" #include "scenelib.h" #include "transformlib.h" #include "instancelib.h" #include "selectionlib.h" #include "traverselib.h" #include "render.h" #include "stringio.h" #include "shaderlib.h" #include "generic/callback.h" #include "signal/signalfwd.h" #include "texturelib.h" #include "xml/ixml.h" #include "dragplanes.h" enum EPatchType { ePatchTypeQuake3, ePatchTypeDoom3, }; extern int g_PatchSubdivideThreshold; #define MIN_PATCH_WIDTH 3 #define MIN_PATCH_HEIGHT 3 extern std::size_t MAX_PATCH_WIDTH; extern std::size_t MAX_PATCH_HEIGHT; #define MAX_PATCH_ROWCTRL (((MAX_PATCH_WIDTH-1)-1)/2) #define MAX_PATCH_COLCTRL (((MAX_PATCH_HEIGHT-1)-1)/2) enum EPatchCap { eCapBevel, eCapEndCap, eCapIBevel, eCapIEndCap, eCapCylinder, }; enum EPatchPrefab { ePlane, eBevel, eEndCap, eCylinder, eDenseCylinder, eVeryDenseCylinder, eSqCylinder, eCone, eSphere, }; enum EMatrixMajor { ROW, COL, }; struct BezierCurve { Vector3 crd; Vector3 left; Vector3 right; }; const std::size_t BEZIERCURVETREE_MAX_INDEX = std::size_t(1) << (std::numeric_limits::digits - 1); struct BezierCurveTree { std::size_t index; BezierCurveTree* left; BezierCurveTree* right; }; inline bool BezierCurveTree_isLeaf(const BezierCurveTree* node) { return node->left == 0 && node->right == 0; } void BezierCurveTree_Delete(BezierCurveTree *pCurve); inline VertexPointer vertexpointer_arbitrarymeshvertex(const ArbitraryMeshVertex* array) { return VertexPointer(VertexPointer::pointer(&array->vertex), sizeof(ArbitraryMeshVertex)); } typedef PatchControl* PatchControlIter; typedef const PatchControl* PatchControlConstIter; inline void copy_ctrl(PatchControlIter ctrl, PatchControlConstIter begin, PatchControlConstIter end) { std::copy(begin, end, ctrl); } const Colour4b colour_corner(0, 255, 0, 255); const Colour4b colour_inside(255, 0, 255, 255); class Patch; class PatchFilter { public: virtual bool filter(const Patch& patch) const = 0; }; bool patch_filtered(Patch& patch); void add_patch_filter(PatchFilter& filter, int mask, bool invert = false); void Patch_addTextureChangedCallback(const SignalHandler& handler); void Patch_textureChanged(); inline void BezierCurveTreeArray_deleteAll(Array& curveTrees) { for(Array::iterator i = curveTrees.begin(); i != curveTrees.end(); ++i) { BezierCurveTree_Delete(*i); } } inline void PatchControlArray_invert(Array& ctrl, std::size_t width, std::size_t height) { Array tmp(width); PatchControlIter from = ctrl.data() + (width * (height - 1)); PatchControlIter to = ctrl.data(); for(std::size_t h = 0; h != ((height - 1) >> 1); ++h, to += width, from -= width) { copy_ctrl(tmp.data(), to, to + width); copy_ctrl(to, from, from + width); copy_ctrl(from, tmp.data(), tmp.data() + width); } } class PatchTesselation { public: PatchTesselation() : m_numStrips(0), m_lenStrips(0), m_nArrayWidth(0), m_nArrayHeight(0) { } Array m_vertices; Array m_indices; std::size_t m_numStrips; std::size_t m_lenStrips; Array m_arrayWidth; std::size_t m_nArrayWidth; Array m_arrayHeight; std::size_t m_nArrayHeight; Array m_curveTreeU; Array m_curveTreeV; }; class RenderablePatchWireframe : public OpenGLRenderable { PatchTesselation& m_tess; public: RenderablePatchWireframe(PatchTesselation& tess) : m_tess(tess) { } void render(RenderStateFlags state) const { { #if NV_DRIVER_BUG glVertexPointer(3, GL_FLOAT, 0, 0); glDrawArrays(GL_TRIANGLE_FAN, 0, 0); #endif std::size_t n = 0; glVertexPointer(3, GL_FLOAT, sizeof(ArbitraryMeshVertex), &m_tess.m_vertices.data()->vertex); for(std::size_t i = 0; i <= m_tess.m_curveTreeV.size(); ++i) { glDrawArrays(GL_LINE_STRIP, GLint(n), GLsizei(m_tess.m_nArrayWidth)); if(i == m_tess.m_curveTreeV.size()) break; if(!BezierCurveTree_isLeaf(m_tess.m_curveTreeV[i])) glDrawArrays(GL_LINE_STRIP, GLint(m_tess.m_curveTreeV[i]->index), GLsizei(m_tess.m_nArrayWidth)); n += (m_tess.m_arrayHeight[i]*m_tess.m_nArrayWidth); } } { const ArbitraryMeshVertex* p = m_tess.m_vertices.data(); std::size_t n = m_tess.m_nArrayWidth * sizeof(ArbitraryMeshVertex); for(std::size_t i = 0; i <= m_tess.m_curveTreeU.size(); ++i) { glVertexPointer(3, GL_FLOAT, GLsizei(n), &p->vertex); glDrawArrays(GL_LINE_STRIP, 0, GLsizei(m_tess.m_nArrayHeight)); if(i == m_tess.m_curveTreeU.size()) break; if(!BezierCurveTree_isLeaf(m_tess.m_curveTreeU[i])) { glVertexPointer(3, GL_FLOAT, GLsizei(n), &(m_tess.m_vertices.data() + (m_tess.m_curveTreeU[i]->index))->vertex); glDrawArrays(GL_LINE_STRIP, 0, GLsizei(m_tess.m_nArrayHeight)); } p += m_tess.m_arrayWidth[i]; } } } }; class RenderablePatchFixedWireframe : public OpenGLRenderable { PatchTesselation& m_tess; public: RenderablePatchFixedWireframe(PatchTesselation& tess) : m_tess(tess) { } void render(RenderStateFlags state) const { glVertexPointer(3, GL_FLOAT, sizeof(ArbitraryMeshVertex), &m_tess.m_vertices.data()->vertex); const RenderIndex* strip_indices = m_tess.m_indices.data(); for(std::size_t i = 0; inormal); glVertexAttribPointerARB(c_attr_TexCoord0, 2, GL_FLOAT, 0, sizeof(ArbitraryMeshVertex), &m_tess.m_vertices.data()->texcoord); glVertexAttribPointerARB(c_attr_Tangent, 3, GL_FLOAT, 0, sizeof(ArbitraryMeshVertex), &m_tess.m_vertices.data()->tangent); glVertexAttribPointerARB(c_attr_Binormal, 3, GL_FLOAT, 0, sizeof(ArbitraryMeshVertex), &m_tess.m_vertices.data()->bitangent); } else { glVertexAttribPointerARB(11, 3, GL_FLOAT, 0, sizeof(ArbitraryMeshVertex), &m_tess.m_vertices.data()->normal); glVertexAttribPointerARB(8, 2, GL_FLOAT, 0, sizeof(ArbitraryMeshVertex), &m_tess.m_vertices.data()->texcoord); glVertexAttribPointerARB(9, 3, GL_FLOAT, 0, sizeof(ArbitraryMeshVertex), &m_tess.m_vertices.data()->tangent); glVertexAttribPointerARB(10, 3, GL_FLOAT, 0, sizeof(ArbitraryMeshVertex), &m_tess.m_vertices.data()->bitangent); } } else { glNormalPointer(GL_FLOAT, sizeof(ArbitraryMeshVertex), &m_tess.m_vertices.data()->normal); glTexCoordPointer(2, GL_FLOAT, sizeof(ArbitraryMeshVertex), &m_tess.m_vertices.data()->texcoord); } glVertexPointer(3, GL_FLOAT, sizeof(ArbitraryMeshVertex), &m_tess.m_vertices.data()->vertex); const RenderIndex* strip_indices = m_tess.m_indices.data(); for(std::size_t i = 0; i m_xml_state; typedef Array PatchControlArray; class SavedState : public UndoMemento { public: SavedState( std::size_t width, std::size_t height, const PatchControlArray& ctrl, const char* shader, bool patchDef3, std::size_t subdivisions_x, std::size_t subdivisions_y ) : m_width(width), m_height(height), m_shader(shader), m_ctrl(ctrl), m_patchDef3(patchDef3), m_subdivisions_x(subdivisions_x), m_subdivisions_y(subdivisions_y) { } void release() { delete this; } std::size_t m_width, m_height; CopiedString m_shader; PatchControlArray m_ctrl; bool m_patchDef3; std::size_t m_subdivisions_x; std::size_t m_subdivisions_y; }; public: class Observer { public: virtual void allocate(std::size_t size) = 0; }; private: typedef UniqueSet Observers; Observers m_observers; scene::Node* m_node; AABB m_aabb_local; // local bbox CopiedString m_shader; Shader* m_state; std::size_t m_width; std::size_t m_height; public: bool m_patchDef3; std::size_t m_subdivisions_x; std::size_t m_subdivisions_y; private: UndoObserver* m_undoable_observer; MapFile* m_map; // dynamically allocated array of control points, size is m_width*m_height PatchControlArray m_ctrl; PatchControlArray m_ctrlTransformed; PatchTesselation m_tess; RenderablePatchSolid m_render_solid; RenderablePatchWireframe m_render_wireframe; RenderablePatchFixedWireframe m_render_wireframe_fixed; static Shader* m_state_ctrl; static Shader* m_state_lattice; VertexBuffer m_ctrl_vertices; RenderableVertexBuffer m_render_ctrl; IndexBuffer m_lattice_indices; RenderableIndexBuffer m_render_lattice; bool m_bOverlay; bool m_transformChanged; Callback m_evaluateTransform; Callback m_boundsChanged; void construct() { m_bOverlay = false; m_width = m_height = 0; m_patchDef3 = false; m_subdivisions_x = 0; m_subdivisions_y = 0; check_shader(); captureShader(); m_xml_state.push_back(xml_state_t::eDefault); } public: Callback m_lightsChanged; static int m_CycleCapIndex;// = 0; static EPatchType m_type; STRING_CONSTANT(Name, "Patch"); Patch(scene::Node& node, const Callback& evaluateTransform, const Callback& boundsChanged) : m_node(&node), m_shader(texdef_name_default()), m_state(0), m_undoable_observer(0), m_map(0), m_render_solid(m_tess), m_render_wireframe(m_tess), m_render_wireframe_fixed(m_tess), m_render_ctrl(GL_POINTS, m_ctrl_vertices), m_render_lattice(GL_LINES, m_lattice_indices, m_ctrl_vertices), m_transformChanged(false), m_evaluateTransform(evaluateTransform), m_boundsChanged(boundsChanged) { construct(); } Patch(const Patch& other, scene::Node& node, const Callback& evaluateTransform, const Callback& boundsChanged) : m_node(&node), m_shader(texdef_name_default()), m_state(0), m_undoable_observer(0), m_map(0), m_render_solid(m_tess), m_render_wireframe(m_tess), m_render_wireframe_fixed(m_tess), m_render_ctrl(GL_POINTS, m_ctrl_vertices), m_render_lattice(GL_LINES, m_lattice_indices, m_ctrl_vertices), m_transformChanged(false), m_evaluateTransform(evaluateTransform), m_boundsChanged(boundsChanged) { construct(); m_patchDef3 = other.m_patchDef3; m_subdivisions_x = other.m_subdivisions_x; m_subdivisions_y = other.m_subdivisions_y; setDims(other.m_width, other.m_height); copy_ctrl(m_ctrl.data(), other.m_ctrl.data(), other.m_ctrl.data()+(m_width*m_height)); SetShader(other.m_shader.c_str()); controlPointsChanged(); } Patch(const Patch& other) : XMLImporter(other), XMLExporter(other), TransformNode(other), Bounded(other), Cullable(other), Snappable(), Undoable(other), Filterable(other), Nameable(other), m_state(0), m_undoable_observer(0), m_map(0), m_render_solid(m_tess), m_render_wireframe(m_tess), m_render_wireframe_fixed(m_tess), m_render_ctrl(GL_POINTS, m_ctrl_vertices), m_render_lattice(GL_LINES, m_lattice_indices, m_ctrl_vertices), m_transformChanged(false), m_evaluateTransform(other.m_evaluateTransform), m_boundsChanged(other.m_boundsChanged) { m_bOverlay = false; m_patchDef3 = other.m_patchDef3; m_subdivisions_x = other.m_subdivisions_x; m_subdivisions_y = other.m_subdivisions_y; setDims(other.m_width, other.m_height); copy_ctrl(m_ctrl.data(), other.m_ctrl.data(), other.m_ctrl.data()+(m_width*m_height)); SetShader(other.m_shader.c_str()); controlPointsChanged(); } ~Patch() { BezierCurveTreeArray_deleteAll(m_tess.m_curveTreeU); BezierCurveTreeArray_deleteAll(m_tess.m_curveTreeV); releaseShader(); ASSERT_MESSAGE(m_observers.empty(), "Patch::~Patch: observers still attached"); } InstanceCounter m_instanceCounter; void instanceAttach(const scene::Path& path) { if(++m_instanceCounter.m_count == 1) { m_state->incrementUsed(); m_map = path_find_mapfile(path.begin(), path.end()); m_undoable_observer = GlobalUndoSystem().observer(this); GlobalFilterSystem().registerFilterable(*this); } else { ASSERT_MESSAGE(path_find_mapfile(path.begin(), path.end()) == m_map, "node is instanced across more than one file"); } } void instanceDetach(const scene::Path& path) { if(--m_instanceCounter.m_count == 0) { m_map = 0; m_undoable_observer = 0; GlobalUndoSystem().release(this); GlobalFilterSystem().unregisterFilterable(*this); m_state->decrementUsed(); } } const char* name() const { return "patch"; } void attach(const NameCallback& callback) { } void detach(const NameCallback& callback) { } void attach(Observer* observer) { observer->allocate(m_width * m_height); m_observers.insert(observer); } void detach(Observer* observer) { m_observers.erase(observer); } void updateFiltered() { if(m_node != 0) { if(patch_filtered(*this)) { m_node->enable(scene::Node::eFiltered); } else { m_node->disable(scene::Node::eFiltered); } } } void onAllocate(std::size_t size) { for(Observers::iterator i = m_observers.begin(); i != m_observers.end(); ++i) { (*i)->allocate(size); } } const Matrix4& localToParent() const { return g_matrix4_identity; } const AABB& localAABB() const { return m_aabb_local; } VolumeIntersectionValue intersectVolume(const VolumeTest& test, const Matrix4& localToWorld) const { return test.TestAABB(m_aabb_local, localToWorld); } void render_solid(Renderer& renderer, const VolumeTest& volume, const Matrix4& localToWorld) const { renderer.SetState(m_state, Renderer::eFullMaterials); renderer.addRenderable(m_render_solid, localToWorld); } void render_wireframe(Renderer& renderer, const VolumeTest& volume, const Matrix4& localToWorld) const { renderer.SetState(m_state, Renderer::eFullMaterials); if(m_patchDef3) { renderer.addRenderable(m_render_wireframe_fixed, localToWorld); } else { renderer.addRenderable(m_render_wireframe, localToWorld); } } void render_component(Renderer& renderer, const VolumeTest& volume, const Matrix4& localToWorld) const { renderer.SetState(m_state_lattice, Renderer::eWireframeOnly); renderer.SetState(m_state_lattice, Renderer::eFullMaterials); renderer.addRenderable(m_render_lattice, localToWorld); renderer.SetState(m_state_ctrl, Renderer::eWireframeOnly); renderer.SetState(m_state_ctrl, Renderer::eFullMaterials); renderer.addRenderable(m_render_ctrl, localToWorld); } void testSelect(Selector& selector, SelectionTest& test) { SelectionIntersection best; IndexPointer::index_type* pIndex = m_tess.m_indices.data(); for(std::size_t s=0; s TransformChangedCaller; void evaluateTransform() { if(m_transformChanged) { m_transformChanged = false; revertTransform(); m_evaluateTransform(); } } void revertTransform() { m_ctrlTransformed = m_ctrl; } void freezeTransform() { undoSave(); evaluateTransform(); ASSERT_MESSAGE(m_ctrlTransformed.size() == m_ctrl.size(), "Patch::freeze: size mismatch"); std::copy(m_ctrlTransformed.begin(), m_ctrlTransformed.end(), m_ctrl.begin()); } void controlPointsChanged() { transformChanged(); evaluateTransform(); UpdateCachedData(); } bool isValid() const; void snapto(float snap) { undoSave(); for(PatchControlIter i = m_ctrl.data(); i != m_ctrl.data() + m_ctrl.size(); ++i) { vector3_snap((*i).m_vertex, snap); } controlPointsChanged(); } void RenderDebug(RenderStateFlags state) const; void RenderNormals(RenderStateFlags state) const; void pushElement(const XMLElement& element) { switch(m_xml_state.back().state()) { case xml_state_t::eDefault: ASSERT_MESSAGE(string_equal(element.name(), "patch"), "parse error"); m_xml_state.push_back(xml_state_t::ePatch); break; case xml_state_t::ePatch: if(string_equal(element.name(), "matrix")) { setDims(atoi(element.attribute("width")), atoi(element.attribute("height"))); m_xml_state.push_back(xml_state_t::eMatrix); } else if(string_equal(element.name(), "shader")) { m_xml_state.push_back(xml_state_t::eShader); } break; default: ERROR_MESSAGE("parse error"); } } void popElement(const char* name) { switch(m_xml_state.back().state()) { case xml_state_t::eDefault: ERROR_MESSAGE("parse error"); break; case xml_state_t::ePatch: break; case xml_state_t::eMatrix: { StringTokeniser content(m_xml_state.back().content()); for(PatchControlIter i = m_ctrl.data(), end = m_ctrl.data() + m_ctrl.size(); i != end; ++i) { (*i).m_vertex[0] = string_read_float(content.getToken()); (*i).m_vertex[1] = string_read_float(content.getToken()); (*i).m_vertex[2] = string_read_float(content.getToken()); (*i).m_texcoord[0] = string_read_float(content.getToken()); (*i).m_texcoord[1] = string_read_float(content.getToken()); } controlPointsChanged(); } break; case xml_state_t::eShader: { SetShader(m_xml_state.back().content()); } break; default: ERROR_MESSAGE("parse error"); } ASSERT_MESSAGE(!m_xml_state.empty(), "popping empty stack"); m_xml_state.pop_back(); } std::size_t write(const char* buffer, std::size_t length) { switch(m_xml_state.back().state()) { case xml_state_t::eDefault: break; case xml_state_t::ePatch: break; case xml_state_t::eMatrix: case xml_state_t::eShader: return m_xml_state.back().write(buffer, length); break; default: ERROR_MESSAGE("parse error"); } return length; } void exportXML(XMLImporter& importer) { StaticElement patchElement("patch"); importer.pushElement(patchElement); { const StaticElement element("shader"); importer.pushElement(element); importer.write(m_shader.c_str(), strlen(m_shader.c_str())); importer.popElement(element.name()); } { char width[16], height[16]; sprintf(width, "%u", Unsigned(m_width)); sprintf(height, "%u", Unsigned(m_height)); StaticElement element("matrix"); element.insertAttribute("width", width); element.insertAttribute("height", height); importer.pushElement(element); { for(PatchControlIter i = m_ctrl.data(), end = m_ctrl.data() + m_ctrl.size(); i != end; ++i) { importer << (*i).m_vertex[0] << ' ' << (*i).m_vertex[1] << ' ' << (*i).m_vertex[2] << ' ' << (*i).m_texcoord[0] << ' ' << (*i).m_texcoord[1]; } } importer.popElement(element.name()); } importer.popElement(patchElement.name()); } void UpdateCachedData(); const char *GetShader() const { return m_shader.c_str(); } void SetShader(const char* name) { ASSERT_NOTNULL(name); if(shader_equal(m_shader.c_str(), name)) return; undoSave(); if(m_instanceCounter.m_count != 0) { m_state->decrementUsed(); } releaseShader(); m_shader = name; captureShader(); if(m_instanceCounter.m_count != 0) { m_state->incrementUsed(); } check_shader(); Patch_textureChanged(); } int getShaderFlags() const { if(m_state != 0) { return m_state->getFlags(); } return 0; } typedef PatchControl* iterator; typedef const PatchControl* const_iterator; iterator begin() { return m_ctrl.data(); } const_iterator begin() const { return m_ctrl.data(); } iterator end() { return m_ctrl.data() + m_ctrl.size(); } const_iterator end() const { return m_ctrl.data() + m_ctrl.size(); } PatchControlArray& getControlPoints() { return m_ctrl; } PatchControlArray& getControlPointsTransformed() { return m_ctrlTransformed; } void setDims (std::size_t w, std::size_t h); std::size_t getWidth() const { return m_width; } std::size_t getHeight() const { return m_height; } PatchControl& ctrlAt(std::size_t row, std::size_t col) { return m_ctrl[row*m_width+col]; } const PatchControl& ctrlAt(std::size_t row, std::size_t col) const { return m_ctrl[row*m_width+col]; } void ConstructPrefab(const AABB& aabb, EPatchPrefab eType, int axis, std::size_t width = 3, std::size_t height = 3); void constructPlane(const AABB& aabb, int axis, std::size_t width, std::size_t height); void InvertMatrix(); void TransposeMatrix(); void Redisperse(EMatrixMajor mt); void InsertRemove(bool bInsert, bool bColumn, bool bFirst); Patch* MakeCap(Patch* patch, EPatchCap eType, EMatrixMajor mt, bool bFirst); void ConstructSeam(EPatchCap eType, Vector3* p, std::size_t width); void FlipTexture(int nAxis); void TranslateTexture(float s, float t); void ScaleTexture(float s, float t); void RotateTexture(float angle); void SetTextureRepeat(float s, float t); // call with s=1 t=1 for FIT void CapTexture(); void NaturalTexture(); void ProjectTexture(int nAxis); void undoSave() { if(m_map != 0) { m_map->changed(); } if(m_undoable_observer != 0) { m_undoable_observer->save(this); } } UndoMemento* exportState() const { return new SavedState(m_width, m_height, m_ctrl, m_shader.c_str(), m_patchDef3, m_subdivisions_x, m_subdivisions_y); } void importState(const UndoMemento* state) { undoSave(); const SavedState& other = *(static_cast(state)); // begin duplicate of SavedState copy constructor, needs refactoring // copy construct { m_width = other.m_width; m_height = other.m_height; SetShader(other.m_shader.c_str()); m_ctrl = other.m_ctrl; onAllocate(m_ctrl.size()); m_patchDef3 = other.m_patchDef3; m_subdivisions_x = other.m_subdivisions_x; m_subdivisions_y = other.m_subdivisions_y; } // end duplicate code Patch_textureChanged(); controlPointsChanged(); } static void constructStatic(EPatchType type) { Patch::m_type = type; Patch::m_state_ctrl = GlobalShaderCache().capture("$POINT"); Patch::m_state_lattice = GlobalShaderCache().capture("$LATTICE"); } static void destroyStatic() { GlobalShaderCache().release("$LATTICE"); GlobalShaderCache().release("$POINT"); } private: void captureShader() { m_state = GlobalShaderCache().capture(m_shader.c_str()); } void releaseShader() { GlobalShaderCache().release(m_shader.c_str()); } void check_shader() { if(!shader_valid(GetShader())) { globalErrorStream() << "patch has invalid texture name: '" << GetShader() << "'\n"; } } void InsertPoints(EMatrixMajor mt, bool bFirst); void RemovePoints(EMatrixMajor mt, bool bFirst); void AccumulateBBox(); void TesselateSubMatrixFixed(ArbitraryMeshVertex* vertices, std::size_t strideX, std::size_t strideY, unsigned int nFlagsX, unsigned int nFlagsY, PatchControl* subMatrix[3][3]); // uses binary trees representing bezier curves to recursively tesselate a bezier sub-patch void TesselateSubMatrix( const BezierCurveTree *BX, const BezierCurveTree *BY, std::size_t offStartX, std::size_t offStartY, std::size_t offEndX, std::size_t offEndY, std::size_t nFlagsX, std::size_t nFlagsY, Vector3& left, Vector3& mid, Vector3& right, Vector2& texLeft, Vector2& texMid, Vector2& texRight, bool bTranspose ); // tesselates the entire surface void BuildTesselationCurves(EMatrixMajor major); void accumulateVertexTangentSpace(std::size_t index, Vector3 tangentX[6], Vector3 tangentY[6], Vector2 tangentS[6], Vector2 tangentT[6], std::size_t index0, std::size_t index1); void BuildVertexArray(); }; inline bool Patch_importHeader(Patch& patch, Tokeniser& tokeniser) { tokeniser.nextLine(); RETURN_FALSE_IF_FAIL(Tokeniser_parseToken(tokeniser, "{")); return true; } inline bool Patch_importShader(Patch& patch, Tokeniser& tokeniser) { // parse shader name tokeniser.nextLine(); const char* texture = tokeniser.getToken(); if(texture == 0) { Tokeniser_unexpectedError(tokeniser, texture, "#texture-name"); return false; } if(string_equal(texture, "NULL")) { patch.SetShader(texdef_name_default()); } else { StringOutputStream shader(string_length(GlobalTexturePrefix_get()) + string_length(texture)); shader << GlobalTexturePrefix_get() << texture; patch.SetShader(shader.c_str()); } return true; } inline bool PatchDoom3_importShader(Patch& patch, Tokeniser& tokeniser) { // parse shader name tokeniser.nextLine(); const char *shader = tokeniser.getToken(); if(shader == 0) { Tokeniser_unexpectedError(tokeniser, shader, "#shader-name"); return false; } if(string_equal(shader, "_emptyname")) { shader = texdef_name_default(); } patch.SetShader(shader); return true; } inline bool Patch_importParams(Patch& patch, Tokeniser& tokeniser) { tokeniser.nextLine(); RETURN_FALSE_IF_FAIL(Tokeniser_parseToken(tokeniser, "(")); // parse matrix dimensions { std::size_t c, r; RETURN_FALSE_IF_FAIL(Tokeniser_getSize(tokeniser, c)); RETURN_FALSE_IF_FAIL(Tokeniser_getSize(tokeniser, r)); patch.setDims(c, r); } if(patch.m_patchDef3) { RETURN_FALSE_IF_FAIL(Tokeniser_getSize(tokeniser, patch.m_subdivisions_x)); RETURN_FALSE_IF_FAIL(Tokeniser_getSize(tokeniser, patch.m_subdivisions_y)); } // ignore contents/flags/value int tmp; RETURN_FALSE_IF_FAIL(Tokeniser_getInteger(tokeniser, tmp)); RETURN_FALSE_IF_FAIL(Tokeniser_getInteger(tokeniser, tmp)); RETURN_FALSE_IF_FAIL(Tokeniser_getInteger(tokeniser, tmp)); RETURN_FALSE_IF_FAIL(Tokeniser_parseToken(tokeniser, ")")); return true; } inline bool Patch_importMatrix(Patch& patch, Tokeniser& tokeniser) { // parse matrix tokeniser.nextLine(); RETURN_FALSE_IF_FAIL(Tokeniser_parseToken(tokeniser, "(")); { for(std::size_t c=0; cm_vertex, best); if(best.valid()) { Selector_add(selector, m_selectable, best); } } void snapto(float snap) { vector3_snap(m_ctrl->m_vertex, snap); } }; class PatchInstance : public Patch::Observer, public scene::Instance, public Selectable, public Renderable, public SelectionTestable, public ComponentSelectionTestable, public ComponentEditable, public ComponentSnappable, public PlaneSelectable, public LightCullable { class TypeCasts { InstanceTypeCastTable m_casts; public: TypeCasts() { InstanceStaticCast::install(m_casts); InstanceContainedCast::install(m_casts); InstanceContainedCast::install(m_casts); InstanceStaticCast::install(m_casts); InstanceStaticCast::install(m_casts); InstanceStaticCast::install(m_casts); InstanceStaticCast::install(m_casts); InstanceStaticCast::install(m_casts); InstanceStaticCast::install(m_casts); InstanceIdentityCast::install(m_casts); InstanceContainedCast::install(m_casts); } InstanceTypeCastTable& get() { return m_casts; } }; Patch& m_patch; typedef std::vector PatchControlInstances; PatchControlInstances m_ctrl_instances; ObservedSelectable m_selectable; DragPlanes m_dragPlanes; mutable RenderablePointVector m_render_selected; mutable AABB m_aabb_component; static Shader* m_state_selpoint; const LightList* m_lightList; TransformModifier m_transform; public: typedef LazyStatic StaticTypeCasts; void lightsChanged() { m_lightList->lightsChanged(); } typedef MemberCaller LightsChangedCaller; STRING_CONSTANT(Name, "PatchInstance"); PatchInstance(const scene::Path& path, scene::Instance* parent, Patch& patch) : Instance(path, parent, this, StaticTypeCasts::instance().get()), m_patch(patch), m_selectable(SelectedChangedCaller(*this)), m_dragPlanes(SelectedChangedComponentCaller(*this)), m_render_selected(GL_POINTS), m_transform(Patch::TransformChangedCaller(m_patch), ApplyTransformCaller(*this)) { m_patch.instanceAttach(Instance::path()); m_patch.attach(this); m_lightList = &GlobalShaderCache().attach(*this); m_patch.m_lightsChanged = LightsChangedCaller(*this); Instance::setTransformChangedCallback(LightsChangedCaller(*this)); } ~PatchInstance() { Instance::setTransformChangedCallback(Callback()); m_patch.m_lightsChanged = Callback(); GlobalShaderCache().detach(*this); m_patch.detach(this); m_patch.instanceDetach(Instance::path()); } void selectedChanged(const Selectable& selectable) { GlobalSelectionSystem().getObserver(SelectionSystem::ePrimitive)(selectable); GlobalSelectionSystem().onSelectedChanged(*this, selectable); Instance::selectedChanged(); } typedef MemberCaller1 SelectedChangedCaller; void selectedChangedComponent(const Selectable& selectable) { GlobalSelectionSystem().getObserver(SelectionSystem::eComponent)(selectable); GlobalSelectionSystem().onComponentSelection(*this, selectable); } typedef MemberCaller1 SelectedChangedComponentCaller; Patch& getPatch() { return m_patch; } Bounded& get(NullType) { return m_patch; } Cullable& get(NullType) { return m_patch; } Transformable& get(NullType) { return m_transform; } static void constructStatic() { m_state_selpoint = GlobalShaderCache().capture("$SELPOINT"); } static void destroyStatic() { GlobalShaderCache().release("$SELPOINT"); } void allocate(std::size_t size) { m_ctrl_instances.clear(); m_ctrl_instances.reserve(size); for(Patch::iterator i = m_patch.begin(); i != m_patch.end(); ++i) { m_ctrl_instances.push_back(PatchControlInstance(&(*i), SelectedChangedComponentCaller(*this))); } } void setSelected(bool select) { m_selectable.setSelected(select); } bool isSelected() const { return m_selectable.isSelected(); } void update_selected() const { m_render_selected.clear(); Patch::iterator ctrl = m_patch.getControlPointsTransformed().begin(); for(PatchControlInstances::const_iterator i = m_ctrl_instances.begin(); i != m_ctrl_instances.end(); ++i, ++ctrl) { if((*i).m_selectable.isSelected()) { const Colour4b colour_selected(0, 0, 255, 255); m_render_selected.push_back(PointVertex(reinterpret_cast((*ctrl).m_vertex), colour_selected)); } } } #if 0 void render(Renderer& renderer, const VolumeTest& volume) const { if(GlobalSelectionSystem().Mode() == SelectionSystem::eComponent && m_selectable.isSelected()) { renderer.Highlight(Renderer::eFace, false); m_patch.render(renderer, volume, localToWorld()); if(GlobalSelectionSystem().ComponentMode() == SelectionSystem::eVertex) { renderer.Highlight(Renderer::ePrimitive, false); m_patch.render_component(renderer, volume, localToWorld()); renderComponentsSelected(renderer, volume); } } else m_patch.render(renderer, volume, localToWorld()); } #endif void renderSolid(Renderer& renderer, const VolumeTest& volume) const { m_patch.evaluateTransform(); renderer.setLights(*m_lightList); m_patch.render_solid(renderer, volume, localToWorld()); renderComponentsSelected(renderer, volume); } void renderWireframe(Renderer& renderer, const VolumeTest& volume) const { m_patch.evaluateTransform(); m_patch.render_wireframe(renderer, volume, localToWorld()); renderComponentsSelected(renderer, volume); } void renderComponentsSelected(Renderer& renderer, const VolumeTest& volume) const { m_patch.evaluateTransform(); update_selected(); if(!m_render_selected.empty()) { renderer.Highlight(Renderer::ePrimitive, false); renderer.SetState(m_state_selpoint, Renderer::eWireframeOnly); renderer.SetState(m_state_selpoint, Renderer::eFullMaterials); renderer.addRenderable(m_render_selected, localToWorld()); } } void renderComponents(Renderer& renderer, const VolumeTest& volume) const { m_patch.evaluateTransform(); if(GlobalSelectionSystem().ComponentMode() == SelectionSystem::eVertex) { m_patch.render_component(renderer, volume, localToWorld()); } } void testSelect(Selector& selector, SelectionTest& test) { test.BeginMesh(localToWorld(), true); m_patch.testSelect(selector, test); } void selectCtrl(bool select) { for(PatchControlInstances::iterator i = m_ctrl_instances.begin(); i != m_ctrl_instances.end(); ++i) { (*i).m_selectable.setSelected(select); } } bool isSelectedComponents() const { for(PatchControlInstances::const_iterator i = m_ctrl_instances.begin(); i != m_ctrl_instances.end(); ++i) { if((*i).m_selectable.isSelected()) { return true; } } return false; } void setSelectedComponents(bool select, SelectionSystem::EComponentMode mode) { if(mode == SelectionSystem::eVertex) { selectCtrl(select); } else if(mode == SelectionSystem::eFace) { m_dragPlanes.setSelected(select); } } const AABB& getSelectedComponentsBounds() const { m_aabb_component = AABB(); for(PatchControlInstances::const_iterator i = m_ctrl_instances.begin(); i != m_ctrl_instances.end(); ++i) { if((*i).m_selectable.isSelected()) { aabb_extend_by_point_safe(m_aabb_component, (*i).m_ctrl->m_vertex); } } return m_aabb_component; } void testSelectComponents(Selector& selector, SelectionTest& test, SelectionSystem::EComponentMode mode) { test.BeginMesh(localToWorld()); switch(mode) { case SelectionSystem::eVertex: { for(PatchControlInstances::iterator i = m_ctrl_instances.begin(); i != m_ctrl_instances.end(); ++i) { (*i).testSelect(selector, test); } } break; default: break; } } bool selectedVertices() { for(PatchControlInstances::iterator i = m_ctrl_instances.begin(); i != m_ctrl_instances.end(); ++i) { if((*i).m_selectable.isSelected()) { return true; } } return false; } void transformComponents(const Matrix4& matrix) { if(selectedVertices()) { PatchControlIter ctrl = m_patch.getControlPointsTransformed().begin(); for(PatchControlInstances::iterator i = m_ctrl_instances.begin(); i != m_ctrl_instances.end(); ++i, ++ctrl) { if((*i).m_selectable.isSelected()) { matrix4_transform_point(matrix, (*ctrl).m_vertex); } } m_patch.UpdateCachedData(); } if(m_dragPlanes.isSelected()) // this should only be true when the transform is a pure translation. { m_patch.transform(m_dragPlanes.evaluateTransform(vector4_to_vector3(matrix.t()))); } } void selectPlanes(Selector& selector, SelectionTest& test, const PlaneCallback& selectedPlaneCallback) { test.BeginMesh(localToWorld()); m_dragPlanes.selectPlanes(m_patch.localAABB(), selector, test, selectedPlaneCallback); } void selectReversedPlanes(Selector& selector, const SelectedPlanes& selectedPlanes) { m_dragPlanes.selectReversedPlanes(m_patch.localAABB(), selector, selectedPlanes); } void snapComponents(float snap) { if(selectedVertices()) { m_patch.undoSave(); for(PatchControlInstances::iterator i = m_ctrl_instances.begin(); i != m_ctrl_instances.end(); ++i) { if((*i).m_selectable.isSelected()) { (*i).snapto(snap); } } m_patch.controlPointsChanged(); } } void evaluateTransform() { Matrix4 matrix(m_transform.calculateTransform()); if(m_transform.getType() == TRANSFORM_PRIMITIVE) { m_patch.transform(matrix); } else { transformComponents(matrix); } } void applyTransform() { m_patch.revertTransform(); evaluateTransform(); m_patch.freezeTransform(); } typedef MemberCaller ApplyTransformCaller; bool testLight(const RendererLight& light) const { return light.testAABB(worldAABB()); } }; template class PatchNode : public scene::Node::Symbiot, public scene::Instantiable, public scene::Cloneable { typedef PatchNode Self; class TypeCasts { InstanceTypeCastTable m_casts; public: TypeCasts() { NodeStaticCast::install(m_casts); NodeStaticCast::install(m_casts); NodeContainedCast::install(m_casts); NodeContainedCast::install(m_casts); NodeContainedCast::install(m_casts); NodeContainedCast::install(m_casts); NodeContainedCast::install(m_casts); NodeContainedCast::install(m_casts); NodeContainedCast::install(m_casts); NodeContainedCast::install(m_casts); } InstanceTypeCastTable& get() { return m_casts; } }; scene::Node m_node; InstanceSet m_instances; Patch m_patch; TokenImporter m_importMap; TokenExporter m_exportMap; public: typedef LazyStatic StaticTypeCasts; Snappable& get(NullType) { return m_patch; } TransformNode& get(NullType) { return m_patch; } Patch& get(NullType) { return m_patch; } XMLImporter& get(NullType) { return m_patch; } XMLExporter& get(NullType) { return m_patch; } MapImporter& get(NullType) { return m_importMap; } MapExporter& get(NullType) { return m_exportMap; } Nameable& get(NullType) { return m_patch; } PatchNode(bool patchDef3 = false) : m_node(this, this, StaticTypeCasts::instance().get()), m_patch(m_node, InstanceSetEvaluateTransform::Caller(m_instances), InstanceSet::BoundsChangedCaller(m_instances)), m_importMap(m_patch), m_exportMap(m_patch) { m_patch.m_patchDef3 = patchDef3; } PatchNode(const PatchNode& other) : scene::Node::Symbiot(other), scene::Instantiable(other), scene::Cloneable(other), m_node(this, this, StaticTypeCasts::instance().get()), m_patch(other.m_patch, m_node, InstanceSetEvaluateTransform::Caller(m_instances), InstanceSet::BoundsChangedCaller(m_instances)), m_importMap(m_patch), m_exportMap(m_patch) { } void release() { delete this; } scene::Node& node() { return m_node; } Patch& get() { return m_patch; } const Patch& get() const { return m_patch; } scene::Node& clone() const { return (new PatchNode(*this))->node(); } scene::Instance* create(const scene::Path& path, scene::Instance* parent) { return new PatchInstance(path, parent, m_patch); } void forEachInstance(const scene::Instantiable::Visitor& visitor) { m_instances.forEachInstance(visitor); } void insert(scene::Instantiable::Observer* observer, const scene::Path& path, scene::Instance* instance) { m_instances.insert(observer, path, instance); } scene::Instance* erase(scene::Instantiable::Observer* observer, const scene::Path& path) { return m_instances.erase(observer, path); } }; typedef PatchNode PatchNodeQuake3; typedef PatchNode PatchNodeDoom3; inline Patch* Node_getPatch(scene::Node& node) { return NodeTypeCast::cast(node); } inline PatchInstance* Instance_getPatch(scene::Instance& instance) { return InstanceTypeCast::cast(instance); } template class PatchSelectedVisitor : public SelectionSystem::Visitor { const Functor& m_functor; public: PatchSelectedVisitor(const Functor& functor) : m_functor(functor) { } void visit(scene::Instance& instance) const { PatchInstance* patch = Instance_getPatch(instance); if(patch != 0) { m_functor(*patch); } } }; template inline void Scene_forEachSelectedPatch(const Functor& functor) { GlobalSelectionSystem().foreachSelected(PatchSelectedVisitor(functor)); } template class PatchVisibleSelectedVisitor : public SelectionSystem::Visitor { const Functor& m_functor; public: PatchVisibleSelectedVisitor(const Functor& functor) : m_functor(functor) { } void visit(scene::Instance& instance) const { PatchInstance* patch = Instance_getPatch(instance); if(patch != 0 && instance.path().top().get().visible()) { m_functor(*patch); } } }; template inline void Scene_forEachVisibleSelectedPatchInstance(const Functor& functor) { GlobalSelectionSystem().foreachSelected(PatchVisibleSelectedVisitor(functor)); } template class PatchForEachWalker : public scene::Graph::Walker { const Functor& m_functor; public: PatchForEachWalker(const Functor& functor) : m_functor(functor) { } bool pre(const scene::Path& path, scene::Instance& instance) const { if(path.top().get().visible()) { Patch* patch = Node_getPatch(path.top()); if(patch != 0) { m_functor(*patch); } } return true; } }; template inline void Scene_forEachVisiblePatch(const Functor& functor) { GlobalSceneGraph().traverse(PatchForEachWalker(functor)); } template class PatchForEachSelectedWalker : public scene::Graph::Walker { const Functor& m_functor; public: PatchForEachSelectedWalker(const Functor& functor) : m_functor(functor) { } bool pre(const scene::Path& path, scene::Instance& instance) const { if(path.top().get().visible()) { Patch* patch = Node_getPatch(path.top()); if(patch != 0 && Instance_getSelectable(instance)->isSelected()) { m_functor(*patch); } } return true; } }; template inline void Scene_forEachVisibleSelectedPatch(const Functor& functor) { GlobalSceneGraph().traverse(PatchForEachSelectedWalker(functor)); } template class PatchForEachInstanceWalker : public scene::Graph::Walker { const Functor& m_functor; public: PatchForEachInstanceWalker(const Functor& functor) : m_functor(functor) { } bool pre(const scene::Path& path, scene::Instance& instance) const { if(path.top().get().visible()) { PatchInstance* patch = Instance_getPatch(instance); if(patch != 0) { m_functor(*patch); } } return true; } }; template inline void Scene_forEachVisiblePatchInstance(const Functor& functor) { GlobalSceneGraph().traverse(PatchForEachInstanceWalker(functor)); } #endif