Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: downloads/ogre_src_v1-9-0/OgreMain/include/OgreManualObject.h @ 148

Last change on this file since 148 was 148, checked in by patricwi, 6 years ago

Added new dependencies for ogre1.9 and cegui0.8

File size: 23.7 KB
Line 
1/*
2-----------------------------------------------------------------------------
3This source file is part of OGRE
4(Object-oriented Graphics Rendering Engine)
5For the latest info, see http://www.ogre3d.org/
6
7Copyright (c) 2000-2013 Torus Knot Software Ltd
8
9Permission is hereby granted, free of charge, to any person obtaining a copy
10of this software and associated documentation files (the "Software"), to deal
11in the Software without restriction, including without limitation the rights
12to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13copies of the Software, and to permit persons to whom the Software is
14furnished to do so, subject to the following conditions:
15
16The above copyright notice and this permission notice shall be included in
17all copies or substantial portions of the Software.
18
19THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25THE SOFTWARE.
26-----------------------------------------------------------------------------
27*/
28
29#ifndef __OgreManualObject_H__
30#define __OgreManualObject_H__
31
32#include "OgrePrerequisites.h"
33#include "OgreMovableObject.h"
34#include "OgreRenderable.h"
35#include "OgreResourceGroupManager.h"
36#include "OgreHeaderPrefix.h"
37
38namespace Ogre
39{
40        /** \addtogroup Core
41        *  @{
42        */
43        /** \addtogroup Scene
44        *  @{
45        */
46        /** Class providing a much simplified interface to generating manual
47                objects with custom geometry.
48        @remarks
49                Building one-off geometry objects manually usually requires getting
50                down and dirty with the vertex buffer and vertex declaration API,
51                which some people find a steep learning curve. This class gives you
52                a simpler interface specifically for the purpose of building a
53                3D object simply and quickly. Note that if you intend to instance your
54                object you will still need to become familiar with the Mesh class.
55        @par
56                This class draws heavily on the interface for OpenGL
57                immediate-mode (glBegin, glVertex, glNormal etc), since this
58                is generally well-liked by people. There are a couple of differences
59                in the results though - internally this class still builds hardware
60                buffers which can be re-used, so you can render the resulting object
61                multiple times without re-issuing all the same commands again.
62                Secondly, the rendering is not immediate, it is still queued just like
63                all OGRE objects. This makes this object more efficient than the
64                equivalent GL immediate-mode commands, so it's feasible to use it for
65                large objects if you really want to.
66        @par
67                To construct some geometry with this object:
68                  -# If you know roughly how many vertices (and indices, if you use them)
69                     you're going to submit, call estimateVertexCount and estimateIndexCount.
70                         This is not essential but will make the process more efficient by saving
71                         memory reallocations.
72                  -# Call begin() to begin entering data
73                  -# For each vertex, call position(), normal(), textureCoord(), colour()
74                     to define your vertex data. Note that each time you call position()
75                         you start a new vertex. Note that the first vertex defines the
76                         components of the vertex - you can't add more after that. For example
77                         if you didn't call normal() in the first vertex, you cannot call it
78                         in any others. You ought to call the same combination of methods per
79                         vertex.
80                  -# If you want to define triangles (or lines/points) by indexing into the vertex list,
81                         you can call index() as many times as you need to define them.
82                         If you don't do this, the class will assume you want triangles drawn
83                         directly as defined by the vertex list, i.e. non-indexed geometry. Note
84                         that stencil shadows are only supported on indexed geometry, and that
85                         indexed geometry is a little faster; so you should try to use it.
86                  -# Call end() to finish entering data.
87                  -# Optionally repeat the begin-end cycle if you want more geometry
88                        using different rendering operation types, or different materials
89            After calling end(), the class will organise the data for that section
90                internally and make it ready to render with. Like any other
91                MovableObject you should attach the object to a SceneNode to make it
92                visible. Other aspects like the relative render order can be controlled
93                using standard MovableObject methods like setRenderQueueGroup.
94        @par
95                You can also use beginUpdate() to alter the geometry later on if you wish.
96                If you do this, you should call setDynamic(true) before your first call
97                to begin(), and also consider using estimateVertexCount / estimateIndexCount
98                if your geometry is going to be growing, to avoid buffer recreation during
99                growth.
100        @par
101                Note that like all OGRE geometry, triangles should be specified in
102                anti-clockwise winding order (whether you're doing it with just
103                vertices, or using indexes too). That is to say that the front of the
104                face is the one where the vertices are listed in anti-clockwise order.
105        */
106        class _OgreExport ManualObject : public MovableObject
107        {
108        public:
109                ManualObject(const String& name);
110                virtual ~ManualObject();
111
112                //pre-declare ManualObjectSection
113                class ManualObjectSection;
114
115                /** Completely clear the contents of the object.
116                @remarks
117                        Clearing the contents of this object and rebuilding from scratch
118                        is not the optimal way to manage dynamic vertex data, since the
119                        buffers are recreated. If you want to keep the same structure but
120                        update the content within that structure, use beginUpdate() instead
121                        of clear() begin(). However if you do want to modify the structure
122                        from time to time you can do so by clearing and re-specifying the data.
123                */
124                virtual void clear(void);
125               
126                /** Estimate the number of vertices ahead of time.
127                @remarks
128                        Calling this helps to avoid memory reallocation when you define
129                        vertices. Also very handy when using beginUpdate() to manage dynamic
130                        data - you can make the vertex buffers a little larger than their
131                        initial needs to allow for growth later with this method.
132                */
133                virtual void estimateVertexCount(size_t vcount);
134
135                /** Estimate the number of indices ahead of time.
136                @remarks
137                        Calling this helps to avoid memory reallocation when you define
138                        indices. Also very handy when using beginUpdate() to manage dynamic
139                        data - you can make the index buffer a little larger than the
140                        initial need to allow for growth later with this method.
141                */
142                virtual void estimateIndexCount(size_t icount);
143
144                /** Start defining a part of the object.
145                @remarks
146                        Each time you call this method, you start a new section of the
147                        object with its own material and potentially its own type of
148                        rendering operation (triangles, points or lines for example).
149                @param materialName The name of the material to render this part of the
150                        object with.
151                @param opType The type of operation to use to render.
152                */
153                virtual void begin(const String& materialName,
154                        RenderOperation::OperationType opType = RenderOperation::OT_TRIANGLE_LIST, const String & groupName = ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
155
156                /** Use before defining geometry to indicate that you intend to update the
157                        geometry regularly and want the internal structure to reflect that.
158                */
159                virtual void setDynamic(bool dyn) { mDynamic = dyn; }
160                /** Gets whether this object is marked as dynamic */
161                virtual bool getDynamic() const { return mDynamic; }
162
163                /** Start the definition of an update to a part of the object.
164                @remarks
165                        Using this method, you can update an existing section of the object
166                        efficiently. You do not have the option of changing the operation type
167                        obviously, since it must match the one that was used before.
168                @note If your sections are changing size, particularly growing, use
169                        estimateVertexCount and estimateIndexCount to pre-size the buffers a little
170                        larger than the initial needs to avoid buffer reconstruction.
171                @param sectionIndex The index of the section you want to update. The first
172                        call to begin() would have created section 0, the second section 1, etc.
173                */
174                virtual void beginUpdate(size_t sectionIndex);
175                /** Add a vertex position, starting a new vertex at the same time.
176                @remarks A vertex position is slightly special among the other vertex data
177                        methods like normal() and textureCoord(), since calling it indicates
178                        the start of a new vertex. All other vertex data methods you call
179                        after this are assumed to be adding more information (like normals or
180                        texture coordinates) to the last vertex started with position().
181                */
182                virtual void position(const Vector3& pos);
183                /// @copydoc ManualObject::position(const Vector3&)
184                virtual void position(Real x, Real y, Real z);
185
186                /** Add a vertex normal to the current vertex.
187                @remarks
188                        Vertex normals are most often used for dynamic lighting, and
189                        their components should be normalised.
190                */
191                virtual void normal(const Vector3& norm);
192                /// @copydoc ManualObject::normal(const Vector3&)
193                virtual void normal(Real x, Real y, Real z);
194
195                /** Add a vertex tangent to the current vertex.
196                @remarks
197                        Vertex tangents are most often used for dynamic lighting, and
198                        their components should be normalised.
199                        Also, using tangent() you enable VES_TANGENT vertex semantic, which is not
200                        supported on old non-SM2 cards.
201                */
202                virtual void tangent(const Vector3& tan);
203                /// @copydoc ManualObject::tangent(const Vector3&)
204                virtual void tangent(Real x, Real y, Real z);
205
206                /** Add a texture coordinate to the current vertex.
207                @remarks
208                        You can call this method multiple times between position() calls
209                        to add multiple texture coordinates to a vertex. Each one can have
210                        between 1 and 3 dimensions, depending on your needs, although 2 is
211                        most common. There are several versions of this method for the
212                        variations in number of dimensions.
213                */
214                virtual void textureCoord(Real u);
215                /// @copydoc ManualObject::textureCoord(Real)
216                virtual void textureCoord(Real u, Real v);
217                /// @copydoc ManualObject::textureCoord(Real)
218                virtual void textureCoord(Real u, Real v, Real w);
219                /// @copydoc ManualObject::textureCoord(Real)
220                virtual void textureCoord(Real x, Real y, Real z, Real w);
221                /// @copydoc ManualObject::textureCoord(Real)
222                virtual void textureCoord(const Vector2& uv);
223                /// @copydoc ManualObject::textureCoord(Real)
224                virtual void textureCoord(const Vector3& uvw);
225                /// @copydoc ManualObject::textureCoord(Real)
226                virtual void textureCoord(const Vector4& xyzw);
227
228                /** Add a vertex colour to a vertex.
229                */
230                virtual void colour(const ColourValue& col);
231                /** Add a vertex colour to a vertex.
232                @param r,g,b,a Colour components expressed as floating point numbers from 0-1
233                */
234                virtual void colour(Real r, Real g, Real b, Real a = 1.0f);
235
236                /** Add a vertex index to construct faces / lines / points via indexing
237                        rather than just by a simple list of vertices.
238                @remarks
239                        You will have to call this 3 times for each face for a triangle list,
240                        or use the alternative 3-parameter version. Other operation types
241                        require different numbers of indexes, @see RenderOperation::OperationType.
242                @note
243                        32-bit indexes are not supported on all cards and will only be used
244            when required, if an index is > 65535.
245                @param idx A vertex index from 0 to 4294967295.
246                */
247                virtual void index(uint32 idx);
248                /** Add a set of 3 vertex indices to construct a triangle; this is a
249                        shortcut to calling index() 3 times. It is only valid for triangle
250                        lists.
251                @note
252                        32-bit indexes are not supported on all cards and will only be used
253            when required, if an index is > 65535.
254                @param i1, i2, i3 3 vertex indices from 0 to 4294967295 defining a face.
255                */
256                virtual void triangle(uint32 i1, uint32 i2, uint32 i3);
257                /** Add a set of 4 vertex indices to construct a quad (out of 2
258                        triangles); this is a shortcut to calling index() 6 times,
259                        or triangle() twice. It's only valid for triangle list operations.
260                @note
261                        32-bit indexes are not supported on all cards and will only be used
262            when required, if an index is > 65535.
263                @param i1, i2, i3, i4 4 vertex indices from 0 to 4294967295 defining a quad.
264                */
265                virtual void quad(uint32 i1, uint32 i2, uint32 i3, uint32 i4);
266
267                /// Get the number of vertices in the section currently being defined (returns 0 if no section is in progress).
268                virtual size_t getCurrentVertexCount() const;
269
270                /// Get the number of indices in the section currently being defined (returns 0 if no section is in progress).
271                virtual size_t getCurrentIndexCount() const;
272               
273                /** Finish defining the object and compile the final renderable version.
274                @note
275                        Will return a pointer to the finished section or NULL if the section was discarded (i.e. has zero vertices/indices).
276                */
277                virtual ManualObjectSection* end(void);
278
279                /** Alter the material for a subsection of this object after it has been
280                        specified.
281                @remarks
282                        You specify the material to use on a section of this object during the
283                        call to begin(), however if you want to change the material afterwards
284                        you can do so by calling this method.
285                @param subIndex The index of the subsection to alter
286                @param name The name of the new material to use
287                */
288                virtual void setMaterialName(size_t subIndex, const String& name, const String & group = ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
289
290                /** Convert this object to a Mesh.
291                @remarks
292                        After you've finished building this object, you may convert it to
293                        a Mesh if you want in order to be able to create many instances of
294                        it in the world (via Entity). This is optional, since this instance
295                        can be directly attached to a SceneNode itself, but of course only
296                        one instance of it can exist that way.
297                @note Only objects which use indexed geometry may be converted to a mesh.
298                @param meshName The name to give the mesh
299                @param groupName The resource group to create the mesh in
300                */
301                virtual MeshPtr convertToMesh(const String& meshName, 
302                        const String& groupName = ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
303
304                /** Sets whether or not to use an 'identity' projection.
305                @remarks
306                        Usually ManualObjects will use a projection matrix as determined
307                        by the active camera. However, if they want they can cancel this out
308                        and use an identity projection, which effectively projects in 2D using
309                        a {-1, 1} view space. Useful for overlay rendering. Normally you don't
310                        need to change this. The default is false.
311                @see ManualObject::getUseIdentityProjection
312                */
313                void setUseIdentityProjection(bool useIdentityProjection);
314
315                /** Returns whether or not to use an 'identity' projection.
316                @remarks
317                        Usually ManualObjects will use a projection matrix as determined
318                        by the active camera. However, if they want they can cancel this out
319                        and use an identity projection, which effectively projects in 2D using
320                        a {-1, 1} view space. Useful for overlay rendering. Normally you don't
321                        need to change this.
322                @see ManualObject::setUseIdentityProjection
323                */
324                bool getUseIdentityProjection(void) const { return mUseIdentityProjection; }
325
326                /** Sets whether or not to use an 'identity' view.
327                @remarks
328                        Usually ManualObjects will use a view matrix as determined
329                        by the active camera. However, if they want they can cancel this out
330                        and use an identity matrix, which means all geometry is assumed
331                        to be relative to camera space already. Useful for overlay rendering.
332                        Normally you don't need to change this. The default is false.
333                @see ManualObject::getUseIdentityView
334                */
335                void setUseIdentityView(bool useIdentityView);
336
337                /** Returns whether or not to use an 'identity' view.
338                @remarks
339                        Usually ManualObjects will use a view matrix as determined
340                        by the active camera. However, if they want they can cancel this out
341                        and use an identity matrix, which means all geometry is assumed
342                        to be relative to camera space already. Useful for overlay rendering.
343                        Normally you don't need to change this.
344                @see ManualObject::setUseIdentityView
345                */
346                bool getUseIdentityView(void) const { return mUseIdentityView; }
347
348                /** Sets the bounding box.
349                        @remarks Call this after having finished creating sections to modify the
350                                bounding box. E.g. if you're using ManualObject to create 2D overlays
351                                you can call things function to set an infinite bounding box so that
352                                the object always stays visible when attached.
353                        @see ManualObject::setUseIdentityProjection, ManualObject::setUseIdentityView,
354                                AxisAlignedBox::setInfinite */
355                void setBoundingBox(const AxisAlignedBox& box) { mAABB = box; }
356
357                /** Gets a pointer to a ManualObjectSection, i.e. a part of a ManualObject.
358                */
359                ManualObjectSection* getSection(unsigned int index) const;
360
361                /** Retrieves the number of ManualObjectSection objects making up this ManualObject.
362                */
363                unsigned int getNumSections(void) const;
364                /** Sets whether or not to keep the original declaration order when
365                        queuing the renderables.
366                @remarks
367                        This overrides the default behavior of the rendering queue,
368                        specifically stating the desired order of rendering. Might result in a
369                        performance loss, but lets the user to have more direct control when
370                        creating geometry through this class.
371                @param keepOrder Whether to keep the declaration order or not.
372                */
373                void setKeepDeclarationOrder(bool keepOrder) { mKeepDeclarationOrder = keepOrder; }
374
375                /** Gets whether or not the declaration order is to be kept or not.
376                @return A flag indication if the declaration order will be kept when
377                        queuing the renderables.
378                */
379                bool getKeepDeclarationOrder() const { return mKeepDeclarationOrder; }
380                // MovableObject overrides
381
382                /** @copydoc MovableObject::getMovableType. */
383                const String& getMovableType(void) const;
384                /** @copydoc MovableObject::getBoundingBox. */
385                const AxisAlignedBox& getBoundingBox(void) const;
386                /** @copydoc MovableObject::getBoundingRadius. */
387                Real getBoundingRadius(void) const;
388                /** @copydoc MovableObject::_updateRenderQueue. */
389                void _updateRenderQueue(RenderQueue* queue);
390                /** Implement this method to enable stencil shadows. */
391                EdgeData* getEdgeList(void);
392                /** Overridden member from ShadowCaster. */
393                bool hasEdgeList(void);
394                /** Implement this method to enable stencil shadows. */
395                ShadowRenderableListIterator getShadowVolumeRenderableIterator(
396                        ShadowTechnique shadowTechnique, const Light* light, 
397                        HardwareIndexBufferSharedPtr* indexBuffer, size_t* indexBufferUsedSize,
398                        bool extrudeVertices, Real extrusionDist, unsigned long flags = 0);
399
400
401                /// Built, renderable section of geometry
402                class _OgreExport ManualObjectSection : public Renderable, public MovableAlloc
403                {
404                protected:
405                        ManualObject* mParent;
406                        String mMaterialName;
407                        String mGroupName;
408                        mutable MaterialPtr mMaterial;
409                        RenderOperation mRenderOperation;
410                        bool m32BitIndices;
411
412                       
413                public:
414                        ManualObjectSection(ManualObject* parent, const String& materialName,
415                                RenderOperation::OperationType opType, const String & groupName = ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
416                        virtual ~ManualObjectSection();
417                       
418                        /// Retrieve render operation for manipulation
419                        RenderOperation* getRenderOperation(void);
420                        /// Retrieve the material name in use
421                        const String& getMaterialName(void) const { return mMaterialName; }
422                        /// Retrieve the material group in use
423                        const String& getMaterialGroup(void) const { return mGroupName; }
424                        /// update the material name in use
425                        void setMaterialName(const String& name, const String& groupName = ResourceGroupManager::AUTODETECT_RESOURCE_GROUP_NAME );
426                        /// Set whether we need 32-bit indices
427                        void set32BitIndices(bool n32) { m32BitIndices = n32; }
428                        /// Get whether we need 32-bit indices
429                        bool get32BitIndices() const { return m32BitIndices; }
430                       
431                        // Renderable overrides
432                        /** @copydoc Renderable::getMaterial. */
433                        const MaterialPtr& getMaterial(void) const;
434                        /** @copydoc Renderable::getRenderOperation. */
435                        void getRenderOperation(RenderOperation& op);
436                        /** @copydoc Renderable::getWorldTransforms. */
437                        void getWorldTransforms(Matrix4* xform) const;
438                        /** @copydoc Renderable::getSquaredViewDepth. */
439                        Real getSquaredViewDepth(const Ogre::Camera *) const;
440                        /** @copydoc Renderable::getLights. */
441                        const LightList &getLights(void) const;
442
443
444                                       
445                };
446                /** Nested class to allow shadows. */
447                class _OgreExport ManualObjectSectionShadowRenderable : public ShadowRenderable
448                {
449                protected:
450                        ManualObject* mParent;
451                        // Shared link to position buffer
452                        HardwareVertexBufferSharedPtr mPositionBuffer;
453                        // Shared link to w-coord buffer (optional)
454                        HardwareVertexBufferSharedPtr mWBuffer;
455
456                public:
457                        ManualObjectSectionShadowRenderable(ManualObject* parent, 
458                                HardwareIndexBufferSharedPtr* indexBuffer, const VertexData* vertexData, 
459                                bool createSeparateLightCap, bool isLightCap = false);
460                        ~ManualObjectSectionShadowRenderable();
461                        /// Overridden from ShadowRenderable
462                        void getWorldTransforms(Matrix4* xform) const;
463                        HardwareVertexBufferSharedPtr getPositionBuffer(void) { return mPositionBuffer; }
464                        HardwareVertexBufferSharedPtr getWBuffer(void) { return mWBuffer; }
465                        /// Overridden from ShadowRenderable
466                        virtual void rebindIndexBuffer(const HardwareIndexBufferSharedPtr& indexBuffer);
467
468                       
469
470                };
471
472                typedef vector<ManualObjectSection*>::type SectionList;
473
474                /// @copydoc MovableObject::visitRenderables
475                void visitRenderables(Renderable::Visitor* visitor, 
476                        bool debugRenderables = false);
477               
478               
479        protected:
480                /// Dynamic?
481                bool mDynamic;
482                /// List of subsections
483                SectionList mSectionList;
484                /// Current section
485                ManualObjectSection* mCurrentSection;
486                /// Are we updating?
487                bool mCurrentUpdating;
488                /// Temporary vertex structure
489                struct TempVertex
490                {
491                        Vector3 position;
492                        Vector3 normal;
493                        Vector3 tangent;
494                        Vector4 texCoord[OGRE_MAX_TEXTURE_COORD_SETS];
495                        ushort texCoordDims[OGRE_MAX_TEXTURE_COORD_SETS];
496                        ColourValue colour;
497                };
498                /// Temp storage
499                TempVertex mTempVertex;
500                /// First vertex indicator
501                bool mFirstVertex;
502                /// Temp vertex data to copy?
503                bool mTempVertexPending;
504                /// System-memory buffer whilst we establish the size required
505                char* mTempVertexBuffer;
506                /// System memory allocation size, in bytes
507                size_t mTempVertexSize;
508                /// System-memory buffer whilst we establish the size required
509                uint32* mTempIndexBuffer;
510                /// System memory allocation size, in bytes
511                size_t mTempIndexSize;
512                /// Current declaration vertex size
513                size_t mDeclSize;
514                /// Estimated vertex count
515                size_t mEstVertexCount;
516                /// Estimated index count
517                size_t mEstIndexCount;
518                /// Current texture coordinate
519                ushort mTexCoordIndex;
520                /// Bounding box
521                AxisAlignedBox mAABB;
522                /// Bounding sphere
523                Real mRadius;
524                /// Any indexed geometry on any sections?
525                bool mAnyIndexed;
526                /// Edge list, used if stencil shadow casting is enabled
527                EdgeData* mEdgeList;
528                /// List of shadow renderables
529                ShadowRenderableList mShadowRenderables;
530                /// Whether to use identity projection for sections
531                bool mUseIdentityProjection;
532                /// Whether to use identity view for sections
533                bool mUseIdentityView;
534                /// Keep declaration order or let the queue optimize it
535                bool mKeepDeclarationOrder;
536
537
538                /// Delete temp buffers and reset init counts
539                virtual void resetTempAreas(void);
540                /// Resize the temp vertex buffer?
541                virtual void resizeTempVertexBufferIfNeeded(size_t numVerts);
542                /// Resize the temp index buffer?
543                virtual void resizeTempIndexBufferIfNeeded(size_t numInds);
544
545                /// Copy current temp vertex into buffer
546                virtual void copyTempVertexToBuffer(void);
547
548        };
549
550
551        /** Factory object for creating ManualObject instances */
552        class _OgreExport ManualObjectFactory : public MovableObjectFactory
553        {
554        protected:
555                MovableObject* createInstanceImpl( const String& name, const NameValuePairList* params);
556        public:
557                ManualObjectFactory() {}
558                ~ManualObjectFactory() {}
559
560                static String FACTORY_TYPE_NAME;
561
562                const String& getType(void) const;
563                void destroyInstance( MovableObject* obj); 
564
565        };
566        /** @} */
567        /** @} */
568}
569
570#include "OgreHeaderSuffix.h"
571
572#endif
573
574
Note: See TracBrowser for help on using the repository browser.