#pragma once #include "AftrConfig.h" #include "ModelFwd.h" #include "AftrGlobals.h" #include "AftrOpenGLIncludes.h" #include "Vector.h" #include "Mat4.h" #include "BoundingBox.h" #include "Tex.h" #include #include #include #include "ModelDataShared.h" #include "ModelMesh.h" #include "ModelMeshDataShared.h" #include "ModelMeshRenderDataGenerator.h" #include "ModelMeshSkin.h" namespace Aftr { class ModelDataShared; class ModelOctreeOfVertices; class ModelKDTreeOfVertices; class AftrGeometryPlane; class AftrGeometryFrustum; class WO; class ModelLODPolicy; class AftrGeometryRayVsFace; enum class AftrGeometricTerm : unsigned char; /** \class Model \brief This class represents a Model. Models are rendered via graphical hardware acceleration (in this case via OpenGL) to a display. This model supports multiple sets of Meshes, each Mesh containing its own skin supporting multi-texturing, shaders, and Material properties. This class represents a Model. Models are rendered via graphical hardware acceleration (in this case via OpenGL) to a display. This implementation is designed to be highly efficient in terms of graphical hardware performance as well as host memory size. All data related to rendering Models (verticies, indicies, textures, shaders) is automatically divided into two parts - 1) sharable data and 2) per instance data. Sharable data is managed via Multiplicity Managers and resides in CPU memory and graphics memory exactly once. All Models utilizing the same underlying geometry share the one instance. Per instance data is modifiable data specific to each instance of a model; this data enables two models sharing the same geometry to be rendered differently (different colors, textures, materials, positions, orientations, etc). For instance, if 25 cars were all loaded from 'ferrari2009.3ds'. The file would be parsed exactly once; the geometry information would be placed in graphics memory exactly once. However, each submesh of each car could utilize a different shader, different material properties, and different sets of multi-textures (bump-mapping, etc). From a user's perspective, the user would percieve 25 individual copies of 'ferrari2009.3ds' existing in the world; this mechanism reduces CPU-side RAM as well as graphics-side RAM. All objects are rendered via OpenGL using indexed geometry objects; namely, Vertex Buffer Objects (VBOs) or Vertex Array Objects (VAs). If the current machine supports VBOs, all non-deformable geometry is placed in a VBO and rendered via the VBO handle; this sends all data to the graphics card exactly once at load time. Any rendering of these objects is then performed on graphics hardware limited only by the graphics hardware. If the current machine does not support VBOs, or if the geometry is deformable, then VAs are used to perform the rendering. VAs send the data to the graphics card each render; however optimized drivers cache the VA data on the graphics card and only need to update the deformed geometry. This implementation is OpenGL 3.0 forward compatible, if enabled, but also supports fixed functionality. Again, all rendering is done with indexed geometry; that is, no display lists, no immediate mode rendering. Model Hierachy: A Model is a collection of one or more submeshes (encapsulated within ModelDataShared). Each submesh (ModelMesh) contains a Skin (ModelMeshSkin) and rendering data (ModelMeshDataShared). Each Skin contains a Texture set (used for mult-texturing), an OpenGL Shader (GLSLShader), Material properties (ambient, diffuse, specular,etc), and a shading type (flat or smooth). Each Texture can be manipulated, i.e., scaled, rotated, translated. OpenGL texture parameters can be set (1D, 2D, 3D, 4D textures, mipmapping, minFilter, magFilter, etc) can be set on a per Texture basis. Each Shader can use any vertex shader along with any fragment shader. Any Skin can be applied to any Mesh. */ class Model { public: static Model* New( WO* parentWO ); static Model* New( WO* parentWO, const std::string& modelFileName, const Vector& scale, MESH_SHADING_TYPE shadingType = MESH_SHADING_TYPE::mstAUTO ); static Model* New( WO* parentWO, ModelDataShared* modelDataShared ); virtual ~Model(); /** /// When a Model is loaded asynchronously, some internal entities, like the ModelDataShared /// and its corresponding ModelMeshSkins are not loaded and one cannot therefore call /// wo->getModel()->getSkin().blah(...) because a worker thread is still parsing the file /// in the background. Trying to call or access these variables will result in a nullptr /// exception and crash the program. There is a *very easy* way to work around this -- simply /// call this method! /// /// This method lets the user enqueue a function to execute as soon as the /// internal data of this WO's Model becomes fully available after asychronous loading completes. /// /// Simply wrap your code in a callable. Then submit the callable to be executed after /// loading completes by calling WO::upon_async_model_loaded( myCallable ); /// Here is a full example that you can copy/paste near your WO::New( fileName ) code:\n\n"; /// WO* a10 = WO::New( ManagerEnvironmentConfiguration::getSMM() + "/models/Aircraft/A10/A10_ScaledActualSize/a10_meshDecimated.obj" ); a10->setPosition( 30, 15, 8 ); //it is OKAY to still set position and orientation -- this is part of the Model a10->getModel()->rotateAboutRelZ( 180.0f * Aftr::DEGtoRAD ); a10->setLabel( "A 10" ); a10->renderOrderType = RENDER_ORDER_TYPE::roOPAQUE; woList->push_back( a10 ); //You still *must* push the a10 into this->worldLst()... a10->upon_async_model_loaded( [a10]() //use a LAMBDA to capture the a10. { //This block of code will be invoked *as soon as* the .obj has completed loading and the ModelDataShared is valid! //It is guranteed this will always execute on the main thread on which the OpenGL Context was created - so OpenGL is invocable. //Now we can call commands that access/set OpenGL specific things inside the model (like the ModelDataShared). ModelMeshSkin& a10Skin = a10->getModel()->getModelDataShared()->getModelMeshes().at( 0 )->getSkins().at( 0 ); //Metal surface colors of the body of the A10 a10Skin.setAmbient( aftrColor4f( 0.3f, 0.3f, 0.3f, 1.0f ) ); //Color of object when it is not in any light a10Skin.setDiffuse( aftrColor4f( 1.0f, 1.0f, 1.0f, 1.0f ) ); //Diffuse color components (ie, matte shading color of this object) a10Skin.setSpecular( aftrColor4f( 0.4f, 0.4f, 0.4f, 1.0f ) ); //Specular color component (ie, how "shiney" it is) a10Skin.setSpecularCoefficient( 10 ); // How "sharp" are the specular highlights (bigger is sharper, 1000 is very sharp, 10 is very dull) //make cockpit transparent ModelMeshSkin& glassCockpit = a10->getModel()->getModelDataShared()->getModelMeshes().at( 1 )->getSkins().at( 0 ); float alpha = 0.4f; //Glass cockpit surface colors (notice the alpha is transparency) glassCockpit.setAmbient( aftrColor4f( 0.3f, 0.3f, 0.3f, alpha ) ); //Color of object when it is not in any light glassCockpit.setDiffuse( aftrColor4f( 1.0f, 1.0f, 1.0f, alpha ) ); //Diffuse color components (ie, matte shading color of this object) glassCockpit.setSpecular( aftrColor4f( 0.4f, 0.4f, 0.4f, alpha ) ); //Specular color component (ie, how "shiney" it is) glassCockpit.setSpecularCoefficient( 80 ); // How "sharp" are the specular highlights (bigger is sharper, 1000 is very sharp, 10 is very dull) //You may also access the bounding box here now that we know the dimensions of the objects we loaded... } ); IMPORTANT NOTE: CAPTURE BY VALUE, NOT REFERENCE, since the reference will likely dangle: //So: //wo->upon_async_model_loaded( [wo]() <--- GOOD, capture by VALUE, NOT gonna crash //wo->upon_async_model_loaded( [&wo]() <--- BAD, capture by reference, gonna crash */ void upon_async_model_loaded( task_to_run_upon_Async_Model_loading_completed&& task ); virtual void render( const Camera& cam ); virtual void renderSelection( const Camera& cam, GLubyte red, GLubyte green, GLubyte blue ); virtual ModelDataShared* getModelDataShared() noexcept; virtual const ModelDataShared* getModelDataShared() const noexcept; virtual const std::string& getFileName() const noexcept; virtual const Vector& getInitialScaleFactor() const noexcept; /** This method restores the Model to its original position, size, and orientation; that is, the position, size, and orientation the model had immediately after being loaded from file. */ virtual void resetModel(); /// Returns a pointer to a list containing all verticies within this model. /// This list is indexed by the corresponding CompositeIndexList. virtual const std::vector< Vector >& getCompositeVertexList() const; /// Returns a pointer to a list containing all indicies into the corresponding /// CompositeVertexList. virtual const std::vector< unsigned int >& getCompositeIndexList() const; /// Returns a pointer to a list containing all verticies within this model. /// This list is indexed by the corresponding CompositeIndexList. virtual const std::vector< Vector >& getCompositeTangentList() const; /// Returns a reference to this Model's current BoundingBox virtual BoundingBox& getBoundingBox(); virtual const BoundingBox& getBoundingBox() const; /** Takes as input an array of 8 Aftr::Vectors. It populates each Vector with the 8 CURRENT points of the bounding box. That is, each of the original verticies of the bounding box are transformed by this model's current display matrix to properly orient each of the 8 verticies. The points are assigned as follows. The bounding box is always centered about the origin and assume the viewer is located at the origin looking along the +x axis. The +z-axis is 'up' and the +y-axis equal to the +x-axis rotated 90 degrees counter clockwise about the +z-axis. verts[0] is "front top left" verts[1] is "front bottom left" verts[2] is "front top right" verts[3] is "front bottom right" verts[4] is "back top left" verts[5] is "back bottom left" verts[6] is "back top right" verts[7] is "back bottom right" Remember that because this box is oriented via this model's display matrix, the box could be flipped around and/or upside down, so verts[0] may not appear to be the "front top left" anymore. */ virtual void getCurrentBoundingBoxVerts( Vector* verts ); /** Takes as input an array of 6 AftrPlanes. It populates each Plane with the 6 CURRENT faces of the bounding box. That is, each of the original planes of the bounding box are transformed by this model's current display matrix to properly orient each of the 6 faces. The faces are assigned as follows. The bounding box is always centered about the origin and assume the viewer is located at the origin looking along the +x axis. The +z-axis is 'up' and the +y-axis equal to the +x-axis rotated 90 degrees counter clockwise about the +z-axis. All face normals point away from the center of the bounding box. faces[0] is "front face" faces[1] is "right face" faces[2] is "back face" faces[3] is "left face" faces[4] is "bottom face" faces[5] is "top face" Remember that because this box is oriented via this model's display matrix, the box could be flipped around and/or upside down, so faces[0] may not appear to be the "front face" anymore. */ virtual void getCurrentBoundingBoxFaces( AftrGeometryPlane* faces ); /** This method uses the data collected from the BoundingBox to compute the "center point" of the Model. This is the point around which scalings and rotations shall occur. */ virtual void calculateFixedPointFromBBox(); /** This method creates a AftrGeometryOBB (oriented bounding box) based off of the model's current orientation and current bounding box size. The lengths of the OBB are determined by calling this instance's getBoundingBox() method. The OBB has a method called getCurrentTransformedVerts(...) which will produce the same results a this instances getCurrentBoundingBoxVerts(...). The OBB is preferred in cases where collision detection / frustum culling is performed; hence both methods are provided as a convenience. */ virtual AftrGeometryOBB getCurrentOBB(); /** Given this model's current global position / orientation and the in passed camera's current position / viewing properties, this method computes the lengths of a billboarded quad in world space (encompassing all of the meshes contained in this model. This billboarded quad is the proper size for rendering this mesh to an texture (from camera) and binding that texture across the quad. The returned X is the billboards width, and the returned Y is the billboards height. This method is utilized by the LOD system. Returns the Lx and Ly lengths of the billboard's geometry when rendered from camera at this model's current global position. Lx and Ly are stored in the returned vector's .x and .y components, respectively. Lx and Ly are in world space. */ Vector getBillboardLxLyInWorldSpace( Camera* cam, Vector* outBillboardCenterPosInWorldSpace = NULL ); Vector getBillboardLxLyInWorldSpaceCachingIntermediateResults( Camera* cam, Vector* outBillboardCenterPosInWorldSpace = NULL ); /** This method takes the current 8 vertices of the this model's current OBB, transforms them from world space to screen space, builds a tight axis-aligned bounding box, and transforms these new 4 screen space verts back to world space. Then the x and y lengths are computed and returned. No z values are returned as x represents the billboard's width and y represents the billboard's height. Optionally, if the user would also like to retrive the w (perspective divide scalar) of this mesh's current centerposition (in world space), an out only point may be passed in. */ Vector getBillboardLxLyInScreenSpace( Camera* cam, Vector* outModelCenterPtInScreenSpace = NULL, float* outModelCenterPtPerspectiveDivideScalar = NULL, Vector* outCenterOfSSBBox = NULL ); Vector getBillboardLxLyInScreenSpaceCachingIntermediateResults( Camera* cam, Vector* outModelCenterPtInScreenSpace = NULL, float* outModelCenterPtPerspectiveDivideScalar = NULL, Vector* outCenterOfSSBBox = NULL ); /** This method moves the model's center (fixedPoint) to the new x, y ,z location. \param newLocation a vector containing the new x, y, z coordinates where the model's fixedPoint shall reside. */ void setPosition( const Vector& newLocation ); void setPosition( float x, float y, float z ); /** This method moves the model's center (fixedPoint) by delta dxdydz \param dxdydz: a vector containing the change in position of x, y, z coordinates where the model's fixedPoint shall reside. */ void moveRelative( const Vector& dxdydz ); /** Returns the Global X,Y,Z coordinate at which this model is currently centered. */ const Vector& getPosition() const noexcept; Vector& getPosition() noexcept; /** Computes a returns a normalized Vector which represents the direction the "front" of the model is facing. For example, if this is a VRML model read in from Blender. Our convention assumes the front of the model points along the positive x-axis and the top of the model points along the positive z-axis. The 'left' of the model will point along the y-axis. \return A Normalized Vector representing the direction the model is currently facing. */ virtual Vector getLookDirection() const; /** Sets this model's look direction (relative x axis) to the in passed Vector. The model's normal direction (relative z axis) will remain in the same relative XZ plane as the new look direction. */ virtual void setLookDirection( const Vector& newLookDir ); /** Computes a returns a normalized Vector which represents the direction perpendicular to the direction the model is facing. For example, if this is a VRML model read in from Blender. Our convention assumes the front of the model points along the positive x-axis and the top of the model points along the positive z-axis. Therefore, this method would return a vector pointing along the +z-axis \return A Normalized Vector representing the direction the model is currently facing. */ virtual Vector getNormalDirection() const; /** Sets this model's normal direction (relative z axis) to the in passed Vector. The model's look direction (relative x axis) will remain in the same relative XZ plane as the new normal direction. */ virtual void setNormalDirection( const Vector& newNormalDir ); virtual Vector getRelXDir() const; ///< Returns local +x axis of this model's coordinate frame virtual Vector getRelYDir() const; ///< Returns local +y axis of this model's coordinate frame virtual Vector getRelZDir() const; ///< Returns local +z axis of this model's coordinate frame void rotateToIdentity(); void rotateAboutRelX( float deltaRadianAngle ); void rotateAboutRelY( float deltaRadianAngle ); void rotateAboutRelZ( float deltaRadianAngle ); void rotateAboutRelAribitraryAxis( const Vector& axisOfRot, float radianDistance ); void rotateAboutGlobalX( float deltaRadianAngle ); void rotateAboutGlobalY( float deltaRadianAngle ); void rotateAboutGlobalZ( float deltaRadianAngle ); /** Rotates the Model about the passed in axis of rotation by the specified number of radians. \param axisOfRotation The axis about which rotation shall take place. \param radianDistance The number of radians to rotation about the axis of rotation. */ void rotateAboutGlobalAribitraryAxis( const Vector& axisOfRot, float radianDistance ); /** Takes the Vector vec as input and transforms vec through this Model's current display matrix. The resulting Vector is returned. For example, if the in passed vec was 1,0,0 (+X-axis), and the Model was currently rotated PI/2 radians about its local Z-axis, then the resultant vector returned would be 0,1,0 (+Y-axis). Of course, this assumes that the Model's initial orientation was facing along the +X-axis, as per the Aftr Engine convention for initial Model orientation. \param vec The Vector to be transformed (multiplied through the current display matrix) of this Model. \return The resultant Vector that has been transformed by this Model's current display matrix. This resultant is NOT normalized; the resultant is only normalized for the case where the input vec was normalized prior to calling this method. */ virtual Vector transformVecThroughCurrentDisplayMatrix( const Vector& vec ); virtual Vector transformVecThroughCurrentDisplayMatrixAndThenAddCurrentPosition( const Vector& vec ); virtual Vector transformVecThroughCurrentDisplayMatrixAfterSubtractingCurrentPosition( const Vector& vec ); virtual Vector transformVecThroughInverseOfCurrentDisplayMatrix( const Vector& vec ); virtual Vector transformVecThroughInverseOfCurrentDisplayMatrixAndThenAddCurrentPosition( const Vector& vec ); virtual Vector transformVecThroughInverseOfCurrentDisplayMatrixAfterSubtractingCurrentPosition( const Vector& vec ); /// Returns the inverse matrix of this models display matrix void calculateInverseOfMyDisplayMatrix( float* resultantInverseMatrix4x4 ); /// Returns true iff any part of this model's bounding box intersects with the in passed viewing frustum. virtual bool isInViewingFrustum( const AftrGeometryFrustum& frustum ); /// \return a Vector containing the scalings of the model in the X,Y, and Z directions, respectively. virtual Vector getScale() const; /** Sets this model's current scale. It is recommending that the user instead specify this vector in the model's constructor. Using this method does not directly change the model's bounding box; if the WO containing this model has an ODE physics body, this method will cause the graphical representation to become larger than the physical representation. If the WO containing this model has no corresponding physics body, then just the bounding box will become invalid. */ virtual void setScale( const Vector& newScaleFactor ); /** This method returns a copy of the current model's display matrix. The caller owns this copy and is responsible for destroying it. 16 floats will be placed in the output param. Vectors are column major. The last column does NOT include the translate amount (this->translateAmt). Use getDisplayMatrixWithPos(...) to include the translate amount in the last column, or manually populate it after this method returns. You may also use getPose() to return the current display matrix within the upper 3x3 and the 4th column (at index 3) will contain the position. The display matrix is a 4x4 matrix such that the upper 3x3 is an orthnormal rotation matrix that transforms a vector from the local model space TO an embedded world space ws_DCM_local (read from right to left). \param float[16] current model's display matrix. */ void getDisplayMatrix( float* outParam4x4DispMat ) const; //Mat4 getDisplayMatrix() const; Mat4& getDisplayMatrix() noexcept; const Mat4& getDisplayMatrix() const noexcept; Mat4 getModelMatrix() const; ///< Returns the full 4x4 model matrix used by a shader, including orientation, position, scale. Mat4 getNormalMatrix( const Camera& cam ) const; ///< Return a 4x4 normal matrix used by shader, same as the deprecated gl_NormalMatrix. /// Returns a Mat4 containing the orientation DCM in the upper 3x3 /// and global position in the 4th column Mat4 getPose() const noexcept; /// Uses the input param's orientation DCM in the upper 3x3 and position /// in the 4th column to adjust the position & orientation of this model. void setPose( const Mat4& orient_with_position ) noexcept; /** This method returns a copy of the current model's display matrix. The caller owns this copy and is responsible for destroying it. 16 floats will be placed in the output param. Vectors are column major. The last column additionally includes the translate amount (this->translateAmt). Use getDisplayMatrix(...) to avoid the translate amount in the last column. \param float[16] current model's display matrix. Last column additional contains the translateAmt of this model. */ void getDisplayMatrixWithPos( float* outParam4x4DispMatWithPos ) const; /** This method returns a copy of the current model's upper 3x3 orientation matrix. The caller owns this copy and is responsible for destroying it. 9 floats will be placed in the output param. Vectors are column major. \param float[9] current model's display matrix. */ void getDisplayMatrix33( float* outParam3x3DispMat ) const; /** This method sets the current model's display matrix. A copy of the matrix passed in by the caller is made; therefore, the caller still owns the matrix he passes in and is responsible for destroying it. \param float[16] display matrix applied to this model. */ void setDisplayMatrix( const float* dispMatrix4x4 ); void setDisplayMatrix( const Mat4& dispMatrix ); /** Sets the point about which all rotations of this model occur. The input is specified as an offset from this model's local coordinate system's center. This is typically (0,0,0), but may be different in cases where, for example, the verticies read from file are not centered about the origin. This method exists to enable a user to explicitly set the model's fixed point if the default is not satisfactory. */ void setFixedPoint( const Vector& fixedPoint ); /** Gets the point about which all rotations of this model occur. This is typically (0,0,0), but may be different in cases where, for example, the verticies read from file are not centered about the origin. This method exists to enable a user to explicitly get the model's fixed point if the default is not satisfactory. */ Vector getFixedPoint() const; /** \returns number of faces in the model (may need to be overloaded for MGLs) */ virtual size_t getFaceCount(); /** If true, the Model's render will enable GL_BLEND; if false, the Model's render will disable GL_BLEND. */ bool isUsingBlending() const { return this->useBlending; } void isUsingBlending( bool useBlending ) { this->useBlending = useBlending; } /** If true, the Model's render will invoke glDepthMask( GL_TRUE ); this will enable writing into the depth buffer. If false, the Model's render will invoke glDepthMask( GL_FALSE ); this will disable writing into the depth buffer. */ bool isUsingDepthMask() const { return this->useDepthMask; } void isUsingDepthMask( bool enableDepthMasking ) { this->useDepthMask = enableDepthMasking; } /** If true, the Model's render will invoke glEnable( GL_DEPTH_TEST ); this will compare rendered fragments against the current depth buffer. If false, the Model's render will invoke glDisable( GL_DEPTH_TEST ); this will disable checking against the depth buffer and draw onto the corresponding fragments regardless of depth. */ bool isUsingDepthTest() const { return this->useDepthTest; } void isUsingDepthTest( bool enableDepthTesting ) { this->useDepthTest = enableDepthTesting; } /** Populates output with the nearest point where the line between from and to intersects the model, defined by Aftr conventions, i.e. uses composite index and vertex lists. This method assumes the Model is centered at the origin with the identity orientation. The user would typically call WO::getNearestPointWhereLineIntersectsMe(...) instead of this method if he wished to intersect a ray at an arbitrary location with a WO(containing this model) at an arbitrary location and orientation. returns AftrGeometricTerm::geoSUCCESS if a point is found returns AftrGeometricTerm::geoERROR if no point is found */ AftrGeometricTerm getNearestPointWhereLineIntersectsMe( const Vector& rayTail, const Vector& rayHead, Vector& output, bool lineSegment = true, bool useOctreeSearchIfAvailable = true, Vector* statistics = NULL, AftrGeometryRayVsFace* hitFace = NULL ); /** This method is O(N) and is not intended to be used by larger models. For larger models, both Octree and KD-tree objects are supported by this model class. It is recommended to use the KD-tree to find the nearest point p to a set of points in set X. The KD-tree is O( log N ) and is more efficient on larger data sets. \return The Index into the Model:compositeVertexList corresponding to the vertex which is closest to the in passed vertex p. The corresponding float is the distance from p to the vector at the returned index. */ std::pair< int, float > findNearestPointInThisModel( const Vector& p ); /** \return An octree of this model's verticies sorted allowing logarithmic searching of closest point. User MUST invoke Model::generateOctreeOfVerticies() prior to a non-NULL octree being returned. */ ModelOctreeOfVertices* getOctreeOfVertices(); ModelKDTreeOfVertices* getKDTreeOfVerticies(); /** Given a AftrGeometryRayVsFace with 3 valid compIndices and a model-space contact point, return the barycentric interpolated UV coordinate corresponding to the 3D model-space contact point. A simple way to properly generate the input face is to invoke Model::getNearestPointWhereLineIntersectsMe( ... AftrGeometryRayVsFace* hitFace...) and pass in a non-NULL AftrGeometryRayVsFace. The resultant hitFace will be populated with this model's composite indices as well as the object-space (model-space) 3D contact point. This method will then compute the precise UV coordinate given the face. The returned vector outUVCoord.xy components correspond to the .uv texture coordinates. Returns True if this model has texture coordinates and was able to properly interpolate (barycentric coords) the UV coordinate of the object-space contact point within the triangle specified by the 3 indices of the inputted AftrGeometryRayVsFace; false, otherwise. */ bool getUVCoordInterpolatedAcrossFace( const AftrGeometryRayVsFace& face, Vector& outUVCoord ) const; /** Given a AftrGeometryRayVsFace with 3 valid compIndices and a model-space contact point, return the barycentric interpolated VertexAttributes corresponding to the 3D model-space contact point. \param face: the face with 3 valid compositeIndices into this model's composite index list as well as a valid contact point. This is used to compute the barycentric coefficients in the x,y,z directions of the face (in object / model space / local space). \param attributeName specifies the name of the vertex attribute (same as the variable within the shader, ie, "tangent" or "quadric". \param atType specifies the type of the attribute to be retrieved; ie, 3 floats? 1 integer? etc. \param outBuf output from this method. This array must contain at least enough space to store the type specified in 'atType'. For example if atType was atVEC3, outBuf must be at least the size of 3*sizeof(GLfloat) (or GLfloat vals[3]). */ bool getPerVertexAttributeInterpolatedAcrossFace( const AftrGeometryRayVsFace& face, const std::string& attributeName, GLSLAttributeType atType, void* outBuf ); /** Returns the WO that points to this Model instance. This value is valid after the Model's constructor completes. */ WO* getParentWO() noexcept; const WO* getParentWO() const noexcept; /** This generates an octree of all the verticies contained by this model's composite vertex and composite index list. The user must invoke this to generate and populate the octree. This is not done by default since an octree is not necessarily needed for normal circumstances. */ void generateOctreeOfVerticies( unsigned int maxNumFacesPerSubnode, unsigned int maxNumSubdivisions ); void generateKDTreeOfVerticies(); /// Prints this models display matrix void printMyDisplayMatrix( std::ostream& os = std::cout, unsigned int decimalPrecision = 3 ); /// Prints the composite vertex list void printMyCompositeVertexList( std::ostream& os = std::cout, unsigned int decimalPrecision = 3 ); /// Prints the composite index list void printMyCompositeIndexList( std::ostream& os = std::cout ); bool renderBBox = false; ///< If true, this model's corresponding bounding box is visible; otherwise, it is not rendered bool renderWireframe = false; ///< If true, this model's wireframe is rendered; otherwise, its triangles are rendered. bool renderOctree = false; ///< If true and this model's octreeVerts is not NULL, the octree is rendered. /** This set is used by the WorldContainer acceleration structure to determine which ModelMeshes fall within the current viewing frustum. This list is completely managed by the WorldContainer and generally does not require any user manipulation. During each frame, visible meshes owned by this Model are inserted into the set. If no meshes are inserted, this model's render is not invoked. If some meshes are inserted, only those meshes are rendered when this model's render() method is invoked. The set is cleared after each render() and repopulated within the WorldContainer::update( Camera* cam ) during the next main iteration. */ std::set< ModelMesh* >& getVisibleMeshesForThisFrame(); /** True iff this instance's std::set< ModelMesh* > visibleMeshesForThisFrame has been populated by the WorldContainer::update() method. This bool is set true from within WorldContainer::update() iff the specific WorldContainer performed ModelMesh culling for accelerated rendering. */ bool useVisibilityMeshesForThisFrame = false; /** This method sets the Model's level of detail (LOD) policy. This value defaults to NULL. In order for this policy to be used during rendering, the ModelLODPolicy::setEnabled( true ), must be invoked by the user. If an existing Policy has already been set, it will be destroyed and replaced by the new policy. */ void setModelLODPolicy( ModelLODPolicy* lodPolicy ); ModelLODPolicy* getModelLODPolicy(); /** Convenience methods that can be used by Model so accessing skins and textures doesn't necessarily need to call: wo->getModel()->getModelDataShared()->getModelMeshes().at( meshIdx )->getSkins().at( skinIdx ); The last param in each of these convenience methods is the meshIdx to access and it defaults to 0. It is typically safe to assume an average model has a mesh at 0. MGLs and subclasses may treat the index in a manner they see fit, including ignoring it as many MGLs may not have internal notions of meshes. */ /** Returns the current skin in use with the modelMeshIdx'th ModelMesh contain within this Model. This is a convenience method and is the same as calling: wo->getModel()->getModelDataShared()->getModelMeshes().at( modelMeshIdx )->getSkin(); For MGLs and MGL subclasses, this method also serves as a polymorphic method that lets a base Model pointer access the internal skin being used even when those MGLs don't have ModelDataShared instances or mesh instances. */ virtual ModelMeshSkin& getSkin( size_t modelMeshIdx = 0 ); virtual const ModelMeshSkin& getSkin( size_t modelMeshIdx = 0 ) const; /** Returns the current set of skins associated with the modelMeshIdx'th ModelMesh contain within this Model. This is a convenience method and is the same as calling: wo->getModel()->getModelDataShared()->getModelMeshes().at( modelMeshIdx )->getSkins(); For MGLs and MGL subclasses, this method also serves as a polymorphic method that lets a base Model pointer access the internal skins being used even when those MGLs don't have ModelDataShared instances or mesh instances. */ virtual std::vector< ModelMeshSkin >& getSkins( size_t modelMeshIdx = 0 ); virtual void addSkin( ModelMeshSkin&& skinToOwn, size_t modelMeshIdx = 0 ); ///< Moves the in passed skin into the specified ModelMesh's skin list virtual void addSkin( const std::string& pathToTexture, size_t modelMeshIdx = 0 ); ///< Creates a Skin using the texture passed in. Inserted into the specified ModelMesh. virtual void addSkin( Tex tex, size_t modelMeshIdx = 0 ); ///< Creates a Skin using the texture. virtual void useNextSkin( size_t modelMeshIdx = 0 ); virtual void usePreviousSkin( size_t modelMeshIdx = 0 ); virtual void useSkinAtIndex( size_t skinIndex, size_t modelMeshIdx = 0 ); virtual size_t getSkinIndex( size_t modelMeshIdx = 0 ); auto begin_face_iter_const() const noexcept; protected: Model( WO* parentWO ); void onCreate(); void onCreate( const std::string& modelFileName, const Vector& scale, const MESH_SHADING_TYPE& shadingType ); void onCreate( ModelDataShared* modelDataShared ); ModelDataShared* modelData = nullptr; ///< This data is managed by the ManagerModelMultiplicity WO* parentWO = nullptr; ///< This WO's model* points to this model. Not owned, do not delete. Mat4 dispMatrix; ///< Aggregation of rotations. Does not include translations or scalings. Translations are access via get/setPosition(...). Vector fixedPoint; ///< scalings and rotations are w.r.t this point (computed based on bBox) Vector translateAmt; ///< current X,Y,Z location of this Model Vector scaleFactor; ///< Scale factor of original size of this model. Only used for rendering, does not effect internal verticies/bounding boxes. bool useScaleFactor = false; ///< True iff, the user has invoked setScaleFactor at least once. bool useBlending = true; ///< When true, the render will glEnable( GL_BLEND ), when false, it will disable GL_BLEND bool useDepthMask = true; ///< When true, the render will glDepthMask(GL_TRUE), when false, it will glDepthMask(GL_FALSE) bool useDepthTest = true; ///< When true, the render will glEnable( GL_DEPTH_TEST ), when false, it will glDisable( GL_DEPTH_TEST ). Only implemented in MGLs that opt-in to checking this flag ModelOctreeOfVertices* octreeVerts = nullptr; ///< Octree of sorted verticies (via indicies) contained in this model. ModelKDTreeOfVertices* kdTreeVerts = nullptr; ///< KD Tree of sorted verticies(via indicies) contained in this model. /** This set is used by the WorldContainer acceleration structure to determine which ModelMeshes fall within the current viewing frustum. This list is completely managed by the WorldContainer and generally does not require any user manipulation. During each frame, visible meshes owned by this Model are inserted into the set. If no meshes are inserted, this model's render is not invoked. If some meshes are inserted, only those meshes are rendered when this model's render() method is invoked. The set is cleared after each render() and repopulated within the WorldContainer::update( Camera* cam ) during the next main iteration. */ std::set< ModelMesh* > visibleMeshesForThisFrame; /** A model is composed of one or more meshes. Each mesh may have one or more associated level of detail (LOD) algorithms. However, each ModelMesh itself, does not have enough global information to make a good decision about how / when it should perform an update / modification to its LOD. This object represents the policy defining when each of this model's mesh's LODs should be updated, and how they should be updated (only a subset of the mesh's LODs may be updated, for example, the LODs with the highest current error). This policy object has useful information, such as how many milliseconds (ms) are available for this model to perform any its LOD updates and render those LODs. This policy also specifies which error metric is used. If this object is not NULL and its setEnabled() is true, then the default behavior is to invoke this policy object's onApplyPolicy() method exactly once inside of this Model's render() method. If onApplyPolicy() is invoked, no other rendering of this model's data will occur (other than what onApplyPolicy() does as well as rendering the bbox or octree, if those are enabled). */ ModelLODPolicy* lodPolicy = nullptr; /** When a Model changes its position, it notifies the WorldContainer instance. This is used by the WorldContainer subclasses, such as the WorldOctree, to identify when a mesh may have changed which node it resides in. This notification is used when the WorldContainer::update(...) method is invoked (immediately before WorldContainer::render( const Camera& cam ) is invoked). When invoked, the WorldContainer acceleration structure, such as the dynamic octree, can update the meshes (belonging to this model) to the proper node to ensure they are efficiently/properly culled during render. This method is invoked by any method in the Model class which manipulates Model::translateAmt. */ virtual void notifyWorldContainerOfAChangeInPosition(); /** When a Model changes its orientation, it notifies the WorldContainer instance. This is used by the WorldContainer subclasses, such as the WorldOctree, to identify when a mesh may have changed which node it resides in. This notification is used when the WorldContainer::update(...) method is invoked (immediately before WorldContainer::render( const Camera& cam ) is invoked). When invoked, the WorldContainer acceleration structure, such as the dynamic octree, can update the meshes (belonging to this model) to the proper node to ensure they are efficiently/properly culled during render. This method is invoked by any method in the Model class which manipulates Model::dispMatrix. */ virtual void notifyWorldContainerOfAChangeInOrientation(); std::vector< task_to_run_upon_Async_Model_loading_completed > submitted_tasks_to_resume_onCreate_after_MDS_completed_main_thread; void run_tasks_to_resume_upon_Async_Model_loading_completed( std::vector< task_to_run_upon_Async_Model_loading_completed >& callables ); }; } //namespace Aftr