Overte C++ Documentation
Procedural.h
1 //
2 // Created by Bradley Austin Davis on 2015/09/05
3 // Copyright 2013-2015 High Fidelity, Inc.
4 //
5 // Distributed under the Apache License, Version 2.0.
6 // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
7 //
8 
9 #pragma once
10 
11 #include <atomic>
12 
13 #include <QtCore/qglobal.h>
14 #include <QtCore/QString>
15 #include <QtCore/QUrl>
16 #include <QtCore/QJsonObject>
17 #include <QtCore/QJsonArray>
18 
19 #include <render/Args.h>
20 #include <gpu/Shader.h>
21 #include <gpu/Pipeline.h>
22 #include <gpu/Batch.h>
23 #include <material-networking/ShaderCache.h>
24 #include <material-networking/TextureCache.h>
25 #include "ProceduralMaterialCache.h"
26 
27 using UniformLambdas = std::list<std::function<void(gpu::Batch& batch)>>;
28 const size_t MAX_PROCEDURAL_TEXTURE_CHANNELS{ 4 };
29 
30 /*@jsdoc
31  * An object containing user-defined uniforms for communicating data to shaders.
32  * @typedef {object} ProceduralUniforms
33  */
34 
35 /*@jsdoc
36  * The data used to define a Procedural shader material.
37  * @typedef {object} ProceduralData
38  * @property {number} version=1 - The version of the procedural shader.
39  * @property {string} vertexShaderURL - A link to a vertex shader. Currently, only GLSL shaders are supported. The shader must implement a different method depending on the version.
40  * If a procedural material contains a vertex shader, the bounding box of the material entity is used to cull the object to which the material is applied.
41  * @property {string} fragmentShaderURL - A link to a fragment shader. Currently, only GLSL shaders are supported. The shader must implement a different method depending on the version.
42  * <code>shaderUrl</code> is an alias.
43  * @property {string[]|Entities.Texture[]} channels=[]] - An array of input textures or entity IDs. Currently, up to 4 are supported. An entity ID may be that of an Image or Web entity.
44  * @property {ProceduralUniforms} uniforms={} - A {@link ProceduralUniforms} object containing all the custom uniforms to be passed to the shader.
45  */
46 
47 struct ProceduralData {
48  static QJsonValue getProceduralData(const QString& proceduralJson);
49  static ProceduralData parse(const QString& userDataJson);
50  void parse(const QJsonObject&);
51 
52  // Rendering object descriptions, from userData
53  uint8_t version { 0 };
54  QUrl fragmentShaderUrl;
55  QUrl vertexShaderUrl;
56  QJsonObject uniforms;
57  QJsonArray channels;
58 };
59 
60 class ProceduralProgramKey {
61 public:
62  enum FlagBit {
63  IS_TRANSPARENT = 0,
64  IS_SKINNED,
65  IS_SKINNED_DQ,
66 
67  NUM_FLAGS
68  };
69  typedef std::bitset<NUM_FLAGS> Flags;
70 
71  Flags _flags;
72 
73  bool isTransparent() const { return _flags[IS_TRANSPARENT]; }
74  bool isSkinned() const { return _flags[IS_SKINNED]; }
75  bool isSkinnedDQ() const { return _flags[IS_SKINNED_DQ]; }
76 
77  ProceduralProgramKey(bool transparent = false, bool isSkinned = false, bool isSkinnedDQ = false) {
78  _flags.set(IS_TRANSPARENT, transparent);
79  _flags.set(IS_SKINNED, isSkinned);
80  _flags.set(IS_SKINNED_DQ, isSkinnedDQ);
81  }
82 };
83 namespace std {
84  template <>
85  struct hash<ProceduralProgramKey> {
86  size_t operator()(const ProceduralProgramKey& key) const {
87  return std::hash<std::bitset<ProceduralProgramKey::FlagBit::NUM_FLAGS>>()(key._flags);
88  }
89  };
90 }
91 inline bool operator==(const ProceduralProgramKey& a, const ProceduralProgramKey& b) {
92  return a._flags == b._flags;
93 }
94 inline bool operator!=(const ProceduralProgramKey& a, const ProceduralProgramKey& b) {
95  return a._flags != b._flags;
96 }
97 
98 // FIXME better encapsulation
99 // FIXME better mechanism for extending to things rendered using shaders other than simple.slv
100 struct Procedural {
101 public:
102  Procedural(bool useAA = true);
103  void setProceduralData(const ProceduralData& proceduralData);
104 
105  bool isReady() const;
106  bool isEnabled() const { return _enabled; }
107  void prepare(gpu::Batch& batch, const glm::vec3& position, const glm::vec3& size, const glm::quat& orientation,
108  const uint64_t& created, const ProceduralProgramKey key = ProceduralProgramKey());
109 
110  glm::vec4 getColor(const glm::vec4& entityColor) const;
111 
112  bool hasVertexShader() const;
113  void setBoundOperator(const std::function<AABox(RenderArgs*)>& boundOperator) { _boundOperator = boundOperator; }
114  bool hasBoundOperator() const { return (bool)_boundOperator; }
115  AABox getBound(RenderArgs* args) { return _boundOperator(args); }
116 
117  void setVertexReplacements(const std::unordered_map<std::string, std::string>& replacements);
118  void setFragmentReplacements(const std::unordered_map<std::string, std::string>& replacements);
119 
120  gpu::Shader::Source _vertexSource;
121  gpu::Shader::Source _vertexSourceSkinned;
122  gpu::Shader::Source _vertexSourceSkinnedDQ;
123  gpu::Shader::Source _opaqueFragmentSource;
124  gpu::Shader::Source _transparentFragmentSource;
125 
126  QString _errorFallbackFragmentPath;
127 
128  gpu::StatePointer _opaqueState { std::make_shared<gpu::State>() };
129  gpu::StatePointer _transparentState { std::make_shared<gpu::State>() };
130 
131  static std::function<void(gpu::StatePointer, bool)> opaqueStencil;
132  static std::function<void(gpu::StatePointer)> transparentStencil;
133 
134  static bool enableProceduralShaders;
135 
136 protected:
137  // DO NOT TOUCH
138  // We have to pack these in a particular way to match the ProceduralCommon.slh
139  // layout.
140  struct StandardInputs {
141  vec4 date;
142  vec4 position;
143  vec4 scale;
144  float timeSinceLastCompile;
145  float timeSinceFirstCompile;
146  float timeSinceEntityCreation;
147  int frameCount;
148  vec4 resolution[4];
149  mat4 orientation;
150  };
151 
152  static_assert(0 == offsetof(StandardInputs, date), "ProceduralOffsets");
153  static_assert(16 == offsetof(StandardInputs, position), "ProceduralOffsets");
154  static_assert(32 == offsetof(StandardInputs, scale), "ProceduralOffsets");
155  static_assert(48 == offsetof(StandardInputs, timeSinceLastCompile), "ProceduralOffsets");
156  static_assert(52 == offsetof(StandardInputs, timeSinceFirstCompile), "ProceduralOffsets");
157  static_assert(56 == offsetof(StandardInputs, timeSinceEntityCreation), "ProceduralOffsets");
158  static_assert(60 == offsetof(StandardInputs, frameCount), "ProceduralOffsets");
159  static_assert(64 == offsetof(StandardInputs, resolution), "ProceduralOffsets");
160  static_assert(128 == offsetof(StandardInputs, orientation), "ProceduralOffsets");
161 
162  // Procedural metadata
163  ProceduralData _data;
164 
165  bool _enabled { false };
166  uint64_t _lastCompile { 0 };
167  uint64_t _firstCompile { 0 };
168  int32_t _frameCount { 0 };
169 
170  // Rendering object descriptions
171  QString _vertexShaderSource;
172  QString _vertexShaderPath;
173  uint64_t _vertexShaderModified { 0 };
174  NetworkShaderPointer _networkVertexShader;
175  QString _fragmentShaderSource;
176  QString _fragmentShaderPath;
177  uint64_t _fragmentShaderModified { 0 };
178  NetworkShaderPointer _networkFragmentShader;
179  bool _shaderDirty { true };
180  bool _uniformsDirty { true };
181 
182  QString _errorFallbackFragmentSource;
183 
184  // Rendering objects
185  UniformLambdas _uniforms;
186  NetworkTexturePointer _channels[MAX_PROCEDURAL_TEXTURE_CHANNELS];
187  Sampler _samplers[MAX_PROCEDURAL_TEXTURE_CHANNELS];
188  std::unordered_map<std::string, std::string> _vertexReplacements;
189  std::unordered_map<std::string, std::string> _fragmentReplacements;
190  std::unordered_map<std::string, int> _slotMap;
191 
192  std::unordered_map<ProceduralProgramKey, gpu::PipelinePointer> _proceduralPipelines;
193  std::unordered_map<ProceduralProgramKey, gpu::PipelinePointer> _errorPipelines;
194  std::unordered_map<ProceduralProgramKey, gpu::PipelinePointer> _disabledPipelines;
195 
196  StandardInputs _standardInputs;
197  gpu::BufferPointer _standardInputsBuffer;
198 
199  // Entity metadata
200  glm::vec3 _entityDimensions;
201  glm::vec3 _entityPosition;
202  glm::mat3 _entityOrientation;
203  uint64_t _entityCreated;
204 
205 private:
206  void setupUniforms();
207 
208  ProceduralProgramKey _prevKey;
209 
210  std::function<AABox(RenderArgs*)> _boundOperator { nullptr };
211 
212  mutable std::mutex _mutex;
213 };
214 
215 namespace graphics {
216 
217 class ProceduralMaterial : public NetworkMaterial {
218 public:
219  ProceduralMaterial() : NetworkMaterial() { initializeProcedural(); }
220  ProceduralMaterial(const NetworkMaterial& material) : NetworkMaterial(material) { initializeProcedural(); }
221 
222  virtual bool isProcedural() const override { return true; }
223  virtual bool isEnabled() const override { return _procedural.isEnabled(); }
224  virtual bool isReady() const override { return _procedural.isReady(); }
225  virtual QString getProceduralString() const override { return _proceduralString; }
226 
227  void setProceduralData(const QString& data) {
228  _proceduralString = data;
229  _procedural.setProceduralData(ProceduralData::parse(data));
230  }
231  virtual glm::vec4 getColor(const glm::vec4& color) const { return _procedural.getColor(color); }
232  virtual bool hasVertexShader() const { return _procedural.hasVertexShader(); }
233  virtual void prepare(gpu::Batch& batch, const glm::vec3& position, const glm::vec3& size, const glm::quat& orientation,
234  const uint64_t& created, const ProceduralProgramKey key = ProceduralProgramKey()) {
235  _procedural.prepare(batch, position, size, orientation, created, key);
236  }
237 
238  virtual void initializeProcedural();
239 
240  void setBoundOperator(const std::function<AABox(RenderArgs*)>& boundOperator) { _procedural.setBoundOperator(boundOperator); }
241  bool hasBoundOperator() const { return _procedural.hasBoundOperator(); }
242  AABox getBound(RenderArgs* args) { return _procedural.getBound(args); }
243 
244 private:
245  QString _proceduralString;
246  Procedural _procedural;
247 };
248 typedef std::shared_ptr<ProceduralMaterial> ProceduralMaterialPointer;
249 
250 }