// // GeometryCache.h // interface/src/renderer // // Created by Andrzej Kapolka on 6/21/13. // Copyright 2013 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // #ifndef hifi_GeometryCache_h #define hifi_GeometryCache_h #include "model-networking/ModelCache.h" #include #include #include #include #include #include #include #include #include #include class SimpleProgramKey; typedef QPair Vec2FloatPair; typedef QPair Vec2FloatPairPair; typedef QPair Vec2Pair; typedef QPair Vec2PairPair; typedef QPair Vec3Pair; typedef QPair Vec4Pair; typedef QPair Vec3PairVec2Pair; typedef QPair Vec3PairVec4; typedef QPair Vec3PairVec4Pair; typedef QPair Vec4PairVec4; typedef QPair Vec4PairVec4Pair; inline uint qHash(const Vec2FloatPairPair& v, uint seed) { // multiply by prime numbers greater than the possible size return qHash(v.first.first.x + 5009 * v.first.first.y + 5011 * v.first.second + 5021 * v.second.first.x + 5023 * v.second.first.y + 5039 * v.second.second); } inline uint qHash(const Vec2Pair& v, uint seed) { // multiply by prime numbers greater than the possible size return qHash(v.first.x + 5009 * v.first.y + 5011 * v.second.x + 5021 * v.second.y, seed); } inline uint qHash(const glm::vec4& v, uint seed) { // multiply by prime numbers greater than the possible size return qHash(v.x + 5009 * v.y + 5011 * v.z + 5021 * v.w, seed); } inline uint qHash(const Vec2PairPair& v, uint seed) { // multiply by prime numbers greater than the possible size return qHash(v.first.first.x + 5009 * v.first.first.y + 5011 * v.first.second.x + 5021 * v.first.second.y + 5023 * v.second.first.x + 5039 * v.second.first.y + 5051 * v.second.second.x + 5059 * v.second.second.y, seed); } inline uint qHash(const Vec3Pair& v, uint seed) { // multiply by prime numbers greater than the possible size return qHash(v.first.x + 5009 * v.first.y + 5011 * v.first.z + 5021 * v.second.x + 5023 * v.second.y + 5039 * v.second.z, seed); } inline uint qHash(const Vec4Pair& v, uint seed) { // multiply by prime numbers greater than the possible size return qHash(v.first.x + 5009 * v.first.y + 5011 * v.first.z + 5021 * v.first.w + 5023 * v.second.x + 5039 * v.second.y + 5051 * v.second.z + 5059 * v.second.w , seed); } inline uint qHash(const Vec3PairVec2Pair& v, uint seed) { // multiply by prime numbers greater than the possible size return qHash(v.first.first.x + 5009 * v.first.first.y + 5011 * v.first.first.z + 5021 * v.first.second.x + 5023 * v.first.second.y + 5039 * v.first.second.z + 5051 * v.second.first.x + 5059 * v.second.first.y + 5077 * v.second.second.x + 5081 * v.second.second.y, seed); } inline uint qHash(const Vec3PairVec4& v, uint seed) { // multiply by prime numbers greater than the possible size return qHash(v.first.first.x + 5009 * v.first.first.y + 5011 * v.first.first.z + 5021 * v.first.second.x + 5023 * v.first.second.y + 5039 * v.first.second.z + 5051 * v.second.x + 5059 * v.second.y + 5077 * v.second.z + 5081 * v.second.w, seed); } inline uint qHash(const Vec3PairVec4Pair& v, uint seed) { // multiply by prime numbers greater than the possible size return qHash(v.first.first.x + 5009 * v.first.first.y + 5011 * v.first.first.z + 5023 * v.first.second.x + 5039 * v.first.second.y + 5051 * v.first.second.z + 5077 * v.second.first.x + 5081 * v.second.first.y + 5087 * v.second.first.z + 5099 * v.second.first.w + 5101 * v.second.second.x + 5107 * v.second.second.y + 5113 * v.second.second.z + 5119 * v.second.second.w, seed); } inline uint qHash(const Vec4PairVec4& v, uint seed) { // multiply by prime numbers greater than the possible size return qHash(v.first.first.x + 5009 * v.first.first.y + 5011 * v.first.first.z + 5021 * v.first.first.w + 5023 * v.first.second.x + 5039 * v.first.second.y + 5051 * v.first.second.z + 5059 * v.first.second.w + 5077 * v.second.x + 5081 * v.second.y + 5087 * v.second.z + 5099 * v.second.w, seed); } inline uint qHash(const Vec4PairVec4Pair& v, uint seed) { // multiply by prime numbers greater than the possible size return qHash(v.first.first.x + 5009 * v.first.first.y + 5011 * v.first.first.z + 5021 * v.first.first.w + 5023 * v.first.second.x + 5039 * v.first.second.y + 5051 * v.first.second.z + 5059 * v.first.second.w + 5077 * v.second.first.x + 5081 * v.second.first.y + 5087 * v.second.first.z + 5099 * v.second.first.w + 5101 * v.second.second.x + 5107 * v.second.second.y + 5113 * v.second.second.z + 5119 * v.second.second.w, seed); } /// Stores cached geometry. class GeometryCache : public Dependency { SINGLETON_DEPENDENCY public: enum Shape { Line, Triangle, Quad, Hexagon, Octagon, Circle, Cube, Sphere, Tetrahedron, Octahedron, Dodecahedron, Icosahedron, Torus, // not yet implemented Cone, Cylinder, NUM_SHAPES, }; /// @param entityShapeEnum: The entity::Shape enumeration for the shape /// whose GeometryCache::Shape is desired. /// @return GeometryCache::NUM_SHAPES in the event of an error; otherwise, /// the GeometryCache::Shape enum which aligns with the /// specified entityShapeEnum static GeometryCache::Shape getShapeForEntityShape(int entityShapeEnum); static QString stringFromShape(GeometryCache::Shape geoShape); static void computeSimpleHullPointListForShape(int entityShape, const glm::vec3 &entityExtents, QVector &outPointList); static uint8_t CUSTOM_PIPELINE_NUMBER; static render::ShapePipelinePointer shapePipelineFactory(const render::ShapePlumber& plumber, const render::ShapeKey& key, gpu::Batch& batch); static void registerShapePipeline() { if (!CUSTOM_PIPELINE_NUMBER) { CUSTOM_PIPELINE_NUMBER = render::ShapePipeline::registerCustomShapePipelineFactory(shapePipelineFactory); } } int allocateID() { return _nextID++; } void releaseID(int id); static const int UNKNOWN_ID; // Bind the pipeline and get the state to render static geometry void bindSimpleProgram(gpu::Batch& batch, bool textured = false, bool transparent = false, bool culled = true, bool unlit = false, bool depthBias = false, bool isAntiAliased = true, bool forward = false); // Get the pipeline to render static geometry static gpu::PipelinePointer getSimplePipeline(bool textured = false, bool transparent = false, bool culled = true, bool unlit = false, bool depthBias = false, bool fading = false, bool isAntiAliased = true, bool forward = false); void bindWebBrowserProgram(gpu::Batch& batch, bool transparent = false); gpu::PipelinePointer getWebBrowserProgram(bool transparent); static void initializeShapePipelines(); render::ShapePipelinePointer getOpaqueShapePipeline() { assert(_simpleOpaquePipeline != nullptr); return _simpleOpaquePipeline; } render::ShapePipelinePointer getTransparentShapePipeline() { assert(_simpleTransparentPipeline != nullptr); return _simpleTransparentPipeline; } render::ShapePipelinePointer getForwardOpaqueShapePipeline() { assert(_forwardSimpleOpaquePipeline != nullptr); return _forwardSimpleOpaquePipeline; } render::ShapePipelinePointer getForwardTransparentShapePipeline() { assert(_forwardSimpleTransparentPipeline != nullptr); return _forwardSimpleTransparentPipeline; } // Static (instanced) geometry void renderShapeInstances(gpu::Batch& batch, Shape shape, size_t count, gpu::BufferPointer& colorBuffer); void renderWireShapeInstances(gpu::Batch& batch, Shape shape, size_t count, gpu::BufferPointer& colorBuffer); void renderFadeShapeInstances(gpu::Batch& batch, Shape shape, size_t count, gpu::BufferPointer& colorBuffer, gpu::BufferPointer& fadeBuffer1, gpu::BufferPointer& fadeBuffer2, gpu::BufferPointer& fadeBuffer3); void renderWireFadeShapeInstances(gpu::Batch& batch, Shape shape, size_t count, gpu::BufferPointer& colorBuffer, gpu::BufferPointer& fadeBuffer1, gpu::BufferPointer& fadeBuffer2, gpu::BufferPointer& fadeBuffer3); void renderSolidShapeInstance(RenderArgs* args, gpu::Batch& batch, Shape shape, const glm::vec4& color = glm::vec4(1), const render::ShapePipelinePointer& pipeline = _simpleOpaquePipeline); void renderSolidShapeInstance(RenderArgs* args, gpu::Batch& batch, Shape shape, const glm::vec3& color, const render::ShapePipelinePointer& pipeline = _simpleOpaquePipeline) { renderSolidShapeInstance(args, batch, shape, glm::vec4(color, 1.0f), pipeline); } void renderWireShapeInstance(RenderArgs* args, gpu::Batch& batch, Shape shape, const glm::vec4& color = glm::vec4(1), const render::ShapePipelinePointer& pipeline = _simpleOpaquePipeline); void renderWireShapeInstance(RenderArgs* args, gpu::Batch& batch, Shape shape, const glm::vec3& color, const render::ShapePipelinePointer& pipeline = _simpleOpaquePipeline) { renderWireShapeInstance(args, batch, shape, glm::vec4(color, 1.0f), pipeline); } void renderSolidFadeShapeInstance(RenderArgs* args, gpu::Batch& batch, Shape shape, const glm::vec4& color, int fadeCategory, float fadeThreshold, const glm::vec3& fadeNoiseOffset, const glm::vec3& fadeBaseOffset, const glm::vec3& fadeBaseInvSize, const render::ShapePipelinePointer& pipeline); void renderWireFadeShapeInstance(RenderArgs* args, gpu::Batch& batch, Shape shape, const glm::vec4& color, int fadeCategory, float fadeThreshold, const glm::vec3& fadeNoiseOffset, const glm::vec3& fadeBaseOffset, const glm::vec3& fadeBaseInvSize, const render::ShapePipelinePointer& pipeline); void renderSolidSphereInstance(RenderArgs* args, gpu::Batch& batch, const glm::vec4& color, const render::ShapePipelinePointer& pipeline = _simpleOpaquePipeline); void renderSolidSphereInstance(RenderArgs* args, gpu::Batch& batch, const glm::vec3& color, const render::ShapePipelinePointer& pipeline = _simpleOpaquePipeline) { renderSolidSphereInstance(args, batch, glm::vec4(color, 1.0f), pipeline); } void renderWireSphereInstance(RenderArgs* args, gpu::Batch& batch, const glm::vec4& color, const render::ShapePipelinePointer& pipeline = _simpleWirePipeline); void renderWireSphereInstance(RenderArgs* args, gpu::Batch& batch, const glm::vec3& color, const render::ShapePipelinePointer& pipeline = _simpleWirePipeline) { renderWireSphereInstance(args, batch, glm::vec4(color, 1.0f), pipeline); } void renderSolidCubeInstance(RenderArgs* args, gpu::Batch& batch, const glm::vec4& color, const render::ShapePipelinePointer& pipeline = _simpleOpaquePipeline); void renderSolidCubeInstance(RenderArgs* args, gpu::Batch& batch, const glm::vec3& color, const render::ShapePipelinePointer& pipeline = _simpleOpaquePipeline) { renderSolidCubeInstance(args, batch, glm::vec4(color, 1.0f), pipeline); } void renderWireCubeInstance(RenderArgs* args, gpu::Batch& batch, const glm::vec4& color, const render::ShapePipelinePointer& pipeline = _simpleWirePipeline); void renderWireCubeInstance(RenderArgs* args, gpu::Batch& batch, const glm::vec3& color, const render::ShapePipelinePointer& pipeline = _simpleWirePipeline) { renderWireCubeInstance(args, batch, glm::vec4(color, 1.0f), pipeline); } // Dynamic geometry void renderShape(gpu::Batch& batch, Shape shape); void renderWireShape(gpu::Batch& batch, Shape shape); void renderShape(gpu::Batch& batch, Shape shape, const glm::vec4& color); void renderWireShape(gpu::Batch& batch, Shape shape, const glm::vec4& color); size_t getShapeTriangleCount(Shape shape); void renderCube(gpu::Batch& batch); void renderWireCube(gpu::Batch& batch); void renderCube(gpu::Batch& batch, const glm::vec4& color); void renderWireCube(gpu::Batch& batch, const glm::vec4& color); size_t getCubeTriangleCount(); void renderSphere(gpu::Batch& batch); void renderWireSphere(gpu::Batch& batch); void renderSphere(gpu::Batch& batch, const glm::vec4& color); void renderWireSphere(gpu::Batch& batch, const glm::vec4& color); size_t getSphereTriangleCount(); void renderGrid(gpu::Batch& batch, const glm::vec2& minCorner, const glm::vec2& maxCorner, int majorRows, int majorCols, float majorEdge, int minorRows, int minorCols, float minorEdge, const glm::vec4& color, bool forward, int id); void renderBevelCornersRect(gpu::Batch& batch, int x, int y, int width, int height, int bevelDistance, const glm::vec4& color, int id); void renderUnitQuad(gpu::Batch& batch, const glm::vec4& color, int id); void renderUnitQuad(gpu::Batch& batch, int id) { renderUnitQuad(batch, glm::vec4(1), id); } void renderQuad(gpu::Batch& batch, int x, int y, int width, int height, const glm::vec4& color, int id) { renderQuad(batch, glm::vec2(x,y), glm::vec2(x + width, y + height), color, id); } // TODO: I think there's a bug in this version of the renderQuad() that's not correctly rebuilding the vbos // if the color changes by the corners are the same, as evidenced by the audio meter which should turn white // when it's clipping void renderQuad(gpu::Batch& batch, const glm::vec2& minCorner, const glm::vec2& maxCorner, const glm::vec4& color, int id); void renderQuad(gpu::Batch& batch, const glm::vec2& minCorner, const glm::vec2& maxCorner, const glm::vec2& texCoordMinCorner, const glm::vec2& texCoordMaxCorner, const glm::vec4& color, int id); void renderQuad(gpu::Batch& batch, const glm::vec3& minCorner, const glm::vec3& maxCorner, const glm::vec4& color, int id); void renderQuad(gpu::Batch& batch, const glm::vec3& topLeft, const glm::vec3& bottomLeft, const glm::vec3& bottomRight, const glm::vec3& topRight, const glm::vec2& texCoordTopLeft, const glm::vec2& texCoordBottomLeft, const glm::vec2& texCoordBottomRight, const glm::vec2& texCoordTopRight, const glm::vec4& color, int id); void renderLine(gpu::Batch& batch, const glm::vec3& p1, const glm::vec3& p2, const glm::vec3& color, int id) { renderLine(batch, p1, p2, color, color, id); } void renderLine(gpu::Batch& batch, const glm::vec3& p1, const glm::vec3& p2, const glm::vec3& color1, const glm::vec3& color2, int id) { renderLine(batch, p1, p2, glm::vec4(color1, 1.0f), glm::vec4(color2, 1.0f), id); } void renderLine(gpu::Batch& batch, const glm::vec3& p1, const glm::vec3& p2, const glm::vec4& color, int id) { renderLine(batch, p1, p2, color, color, id); } void renderLine(gpu::Batch& batch, const glm::vec3& p1, const glm::vec3& p2, const glm::vec4& color1, const glm::vec4& color2, int id); void renderDashedLine(gpu::Batch& batch, const glm::vec3& start, const glm::vec3& end, const glm::vec4& color, int id) { renderDashedLine(batch, start, end, color, 0.05f, 0.025f, id); } void renderDashedLine(gpu::Batch& batch, const glm::vec3& start, const glm::vec3& end, const glm::vec4& color, const float dash_length, const float gap_length, int id); void renderLine(gpu::Batch& batch, const glm::vec2& p1, const glm::vec2& p2, const glm::vec3& color, int id) { renderLine(batch, p1, p2, glm::vec4(color, 1.0f), id); } void renderLine(gpu::Batch& batch, const glm::vec2& p1, const glm::vec2& p2, const glm::vec4& color, int id) { renderLine(batch, p1, p2, color, color, id); } void renderLine(gpu::Batch& batch, const glm::vec2& p1, const glm::vec2& p2, const glm::vec3& color1, const glm::vec3& color2, int id) { renderLine(batch, p1, p2, glm::vec4(color1, 1.0f), glm::vec4(color2, 1.0f), id); } void renderLine(gpu::Batch& batch, const glm::vec2& p1, const glm::vec2& p2, const glm::vec4& color1, const glm::vec4& color2, int id); void updateVertices(int id, const QVector& points, const glm::vec4& color); void updateVertices(int id, const QVector& points, const QVector& colors); void updateVertices(int id, const QVector& points, const glm::vec4& color); void updateVertices(int id, const QVector& points, const QVector& colors); void updateVertices(int id, const QVector& points, const QVector& texCoords, const glm::vec4& color); void renderVertices(gpu::Batch& batch, gpu::Primitive primitiveType, int id); /// Set a batch to the simple pipeline, returning the previous pipeline void useSimpleDrawPipeline(gpu::Batch& batch, bool noBlend = false); struct ShapeVertex { ShapeVertex(const vec3& pos, const vec3& normal, const vec2& uv, const vec3& tangent) : pos(pos), normal(normal), uv(uv), tangent(tangent) {} vec3 pos; vec3 normal; vec2 uv; vec3 tangent; }; struct ShapeData { gpu::BufferView _positionView; gpu::BufferView _normalView; gpu::BufferView _texCoordView; gpu::BufferView _tangentView; gpu::BufferView _indicesView; gpu::BufferView _wireIndicesView; void setupVertices(gpu::BufferPointer& vertexBuffer, const std::vector& vertices); void setupIndices(gpu::BufferPointer& indexBuffer, const geometry::IndexVector& indices, const geometry::IndexVector& wireIndices); void setupBatch(gpu::Batch& batch) const; void draw(gpu::Batch& batch) const; void drawWire(gpu::Batch& batch) const; void drawInstances(gpu::Batch& batch, size_t count) const; void drawWireInstances(gpu::Batch& batch, size_t count) const; }; using VShape = std::array; /// returns ShapeData associated with the specified shape, /// otherwise nullptr in the event of an error. const ShapeData * getShapeData(Shape shape) const; graphics::MeshPointer meshFromShape(Shape geometryShape, glm::vec3 color); private: GeometryCache(); virtual ~GeometryCache(); void buildShapes(); typedef QPair IntPair; typedef QPair VerticesIndices; VShape _shapes; gpu::PipelinePointer _standardDrawPipeline; gpu::PipelinePointer _standardDrawPipelineNoBlend; gpu::BufferPointer _shapeVertices{ std::make_shared() }; gpu::BufferPointer _shapeIndices{ std::make_shared() }; class GridSchema { public: // data is arranged as majorRow, majorCol, minorRow, minorCol glm::vec4 period; glm::vec4 offset; glm::vec4 edge; }; using GridBuffer = gpu::BufferView; void useGridPipeline(gpu::Batch& batch, GridBuffer gridBuffer, bool transparent, bool forward); static std::map, gpu::PipelinePointer> _gridPipelines; class BatchItemDetails { public: static int population; gpu::BufferPointer verticesBuffer; gpu::BufferPointer colorBuffer; gpu::BufferPointer uniformBuffer; gpu::Stream::FormatPointer streamFormat; gpu::BufferStreamPointer stream; int vertices; int vertexSize; bool isCreated; BatchItemDetails(); BatchItemDetails(const GeometryCache::BatchItemDetails& other); ~BatchItemDetails(); void clear(); }; QHash _coneVBOs; int _nextID{ 1 }; QHash _lastRegisteredQuad3DTexture; QHash _registeredQuad3DTextures; QHash _lastRegisteredQuad2DTexture; QHash _registeredQuad2DTextures; QHash _lastRegisteredQuad3D; QHash _registeredQuad3D; QHash _lastRegisteredQuad2D; QHash _registeredQuad2D; QHash _lastRegisteredBevelRects; QHash _registeredBevelRects; QHash _lastRegisteredLine3D; QHash _registeredLine3DVBOs; QHash _lastRegisteredLine2D; QHash _registeredLine2DVBOs; QHash _registeredVertices; QHash _lastRegisteredDashedLines; QHash _registeredDashedLines; QHash _lastRegisteredGridBuffer; QHash _registeredGridBuffers; // FIXME: clean these up static gpu::ShaderPointer _simpleShader; static gpu::ShaderPointer _transparentShader; static gpu::ShaderPointer _unlitShader; static gpu::ShaderPointer _simpleFadeShader; static gpu::ShaderPointer _unlitFadeShader; static gpu::ShaderPointer _forwardSimpleShader; static gpu::ShaderPointer _forwardTransparentShader; static gpu::ShaderPointer _forwardUnlitShader; static gpu::ShaderPointer _forwardSimpleFadeShader; static gpu::ShaderPointer _forwardUnlitFadeShader; static render::ShapePipelinePointer _simpleOpaquePipeline; static render::ShapePipelinePointer _simpleTransparentPipeline; static render::ShapePipelinePointer _forwardSimpleOpaquePipeline; static render::ShapePipelinePointer _forwardSimpleTransparentPipeline; static render::ShapePipelinePointer _simpleOpaqueFadePipeline; static render::ShapePipelinePointer _simpleTransparentFadePipeline; static render::ShapePipelinePointer _simpleWirePipeline; static QHash _simplePrograms; gpu::ShaderPointer _simpleOpaqueWebBrowserShader; gpu::PipelinePointer _simpleOpaqueWebBrowserPipeline; gpu::ShaderPointer _simpleTransparentWebBrowserShader; gpu::PipelinePointer _simpleTransparentWebBrowserPipeline; static render::ShapePipelinePointer getShapePipeline(bool textured = false, bool transparent = false, bool culled = true, bool unlit = false, bool depthBias = false, bool forward = false); static render::ShapePipelinePointer getFadingShapePipeline(bool textured = false, bool transparent = false, bool culled = true, bool unlit = false, bool depthBias = false, bool forward = false); }; #endif // hifi_GeometryCache_h