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[]} channels=[] - An array of input texture URLs 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();
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  uint64_t getFadeStartTime() const { return _fadeStartTime; }
112  bool isFading() const { return _doesFade && _isFading; }
113  void setIsFading(bool isFading) { _isFading = isFading; }
114  void setDoesFade(bool doesFade) { _doesFade = doesFade; }
115 
116  bool hasVertexShader() const;
117  void setBoundOperator(const std::function<AABox(RenderArgs*)>& boundOperator) { _boundOperator = boundOperator; }
118  bool hasBoundOperator() const { return (bool)_boundOperator; }
119  AABox getBound(RenderArgs* args) { return _boundOperator(args); }
120 
121  gpu::Shader::Source _vertexSource;
122  gpu::Shader::Source _vertexSourceSkinned;
123  gpu::Shader::Source _vertexSourceSkinnedDQ;
124  gpu::Shader::Source _opaqueFragmentSource;
125  gpu::Shader::Source _transparentFragmentSource;
126 
127  gpu::StatePointer _opaqueState { std::make_shared<gpu::State>() };
128  gpu::StatePointer _transparentState { std::make_shared<gpu::State>() };
129 
130  static std::function<void(gpu::StatePointer)> opaqueStencil;
131  static std::function<void(gpu::StatePointer)> transparentStencil;
132 
133 protected:
134  // DO NOT TOUCH
135  // We have to pack these in a particular way to match the ProceduralCommon.slh
136  // layout.
137  struct StandardInputs {
138  vec4 date;
139  vec4 position;
140  vec4 scale;
141  float timeSinceLastCompile;
142  float timeSinceFirstCompile;
143  float timeSinceEntityCreation;
144  int frameCount;
145  vec4 resolution[4];
146  mat4 orientation;
147  };
148 
149  static_assert(0 == offsetof(StandardInputs, date), "ProceduralOffsets");
150  static_assert(16 == offsetof(StandardInputs, position), "ProceduralOffsets");
151  static_assert(32 == offsetof(StandardInputs, scale), "ProceduralOffsets");
152  static_assert(48 == offsetof(StandardInputs, timeSinceLastCompile), "ProceduralOffsets");
153  static_assert(52 == offsetof(StandardInputs, timeSinceFirstCompile), "ProceduralOffsets");
154  static_assert(56 == offsetof(StandardInputs, timeSinceEntityCreation), "ProceduralOffsets");
155  static_assert(60 == offsetof(StandardInputs, frameCount), "ProceduralOffsets");
156  static_assert(64 == offsetof(StandardInputs, resolution), "ProceduralOffsets");
157  static_assert(128 == offsetof(StandardInputs, orientation), "ProceduralOffsets");
158 
159  // Procedural metadata
160  ProceduralData _data;
161 
162  bool _enabled { false };
163  uint64_t _lastCompile { 0 };
164  uint64_t _firstCompile { 0 };
165  int32_t _frameCount { 0 };
166 
167  // Rendering object descriptions
168  QString _vertexShaderSource;
169  QString _vertexShaderPath;
170  uint64_t _vertexShaderModified { 0 };
171  NetworkShaderPointer _networkVertexShader;
172  QString _fragmentShaderSource;
173  QString _fragmentShaderPath;
174  uint64_t _fragmentShaderModified { 0 };
175  NetworkShaderPointer _networkFragmentShader;
176  bool _shaderDirty { true };
177  bool _uniformsDirty { true };
178 
179  // Rendering objects
180  UniformLambdas _uniforms;
181  NetworkTexturePointer _channels[MAX_PROCEDURAL_TEXTURE_CHANNELS];
182 
183  std::unordered_map<ProceduralProgramKey, gpu::PipelinePointer> _proceduralPipelines;
184 
185  StandardInputs _standardInputs;
186  gpu::BufferPointer _standardInputsBuffer;
187 
188  // Entity metadata
189  glm::vec3 _entityDimensions;
190  glm::vec3 _entityPosition;
191  glm::mat3 _entityOrientation;
192  uint64_t _entityCreated;
193 
194 private:
195  void setupUniforms();
196 
197  mutable uint64_t _fadeStartTime { 0 };
198  mutable bool _hasStartedFade { false };
199  mutable bool _isFading { false };
200  bool _doesFade { true };
201  ProceduralProgramKey _prevKey;
202 
203  std::function<AABox(RenderArgs*)> _boundOperator { nullptr };
204 
205  mutable std::mutex _mutex;
206 };
207 
208 namespace graphics {
209 
210 class ProceduralMaterial : public NetworkMaterial {
211 public:
212  ProceduralMaterial() : NetworkMaterial() { initializeProcedural(); }
213  ProceduralMaterial(const NetworkMaterial& material) : NetworkMaterial(material) { initializeProcedural(); }
214 
215  virtual bool isProcedural() const override { return true; }
216  virtual bool isEnabled() const override { return _procedural.isEnabled(); }
217  virtual bool isReady() const override { return _procedural.isReady(); }
218  virtual QString getProceduralString() const override { return _proceduralString; }
219 
220  void setProceduralData(const QString& data) {
221  _proceduralString = data;
222  _procedural.setProceduralData(ProceduralData::parse(data));
223  }
224  virtual glm::vec4 getColor(const glm::vec4& color) const { return _procedural.getColor(color); }
225  virtual bool isFading() const { return _procedural.isFading(); }
226  void setIsFading(bool isFading) { _procedural.setIsFading(isFading); }
227  virtual uint64_t getFadeStartTime() const { return _procedural.getFadeStartTime(); }
228  virtual bool hasVertexShader() const { return _procedural.hasVertexShader(); }
229  virtual void prepare(gpu::Batch& batch, const glm::vec3& position, const glm::vec3& size, const glm::quat& orientation,
230  const uint64_t& created, const ProceduralProgramKey key = ProceduralProgramKey()) {
231  _procedural.prepare(batch, position, size, orientation, created, key);
232  }
233 
234  virtual void initializeProcedural();
235 
236  void setBoundOperator(const std::function<AABox(RenderArgs*)>& boundOperator) { _procedural.setBoundOperator(boundOperator); }
237  bool hasBoundOperator() const { return _procedural.hasBoundOperator(); }
238  AABox getBound(RenderArgs* args) { return _procedural.getBound(args); }
239 
240 private:
241  QString _proceduralString;
242  Procedural _procedural;
243 };
244 typedef std::shared_ptr<ProceduralMaterial> ProceduralMaterialPointer;
245 
246 }