OpenClonk
StdMeshMaterial.h
Go to the documentation of this file.
1 /*
2  * OpenClonk, http://www.openclonk.org
3  *
4  * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
5  * Copyright (c) 2009-2016, The OpenClonk Team and contributors
6  *
7  * Distributed under the terms of the ISC license; see accompanying file
8  * "COPYING" for details.
9  *
10  * "Clonk" is a registered trademark of Matthes Bender, used with permission.
11  * See accompanying file "TRADEMARK" for details.
12  *
13  * To redistribute this file separately, substitute the full license texts
14  * for the above references.
15  */
16 
17 #ifndef INC_StdMeshMaterial
18 #define INC_StdMeshMaterial
19 
20 #include "graphics/C4Shader.h"
21 #include "graphics/C4Surface.h"
22 
23 #include <tuple>
24 
25 // TODO: Support more features of OGRE material scripts
26 // Refer to http://www.ogre3d.org/docs/manual/manual_14.html
27 
29 
30 class StdMeshMaterialError: public std::exception
31 {
32 public:
33  StdMeshMaterialError(const StdStrBuf& message, const char* file, unsigned int line);
34  ~StdMeshMaterialError() throw() override = default;
35 
36  const char* what() const throw() override { return Buf.getData(); }
37 
38 protected:
40 };
41 
43 {
44 public:
45  enum Type {
47  AUTO_TEXTURE_MATRIX, // Texture matrix for the i-th texture
48  INT,
54  };
55 
56  enum Auto {
57  // TODO: OGRE auto values
59  };
60 
61  StdMeshMaterialShaderParameter(); // type=FLOAT, value uninitialized
62  StdMeshMaterialShaderParameter(Type type); // value uninitialized
66 
69 
70  Type GetType() const { return type; }
71  void SetType(Type type); // changes type, new value is uninitialized
72 
73  // Getters
74  Auto GetAuto() const { assert(type == AUTO); return a; }
75  int GetInt() const { assert(type == INT || type == AUTO_TEXTURE_MATRIX); return i; }
76  float GetFloat() const { assert(type == FLOAT); return f[0]; }
77  const float* GetFloatv() const { assert(type == FLOAT2 || type == FLOAT3 || type == FLOAT4); return f; }
78  const float* GetMatrix() const { assert(type == MATRIX_4X4); return matrix; }
79 
80  // Setters
81  Auto& GetAuto() { assert(type == AUTO); return a; }
82  int& GetInt() { assert(type == INT || type == AUTO_TEXTURE_MATRIX); return i; }
83  float& GetFloat() { assert(type == FLOAT); return f[0]; }
84  float* GetFloatv() { assert(type == FLOAT2 || type == FLOAT3 || type == FLOAT4); return f; }
85  float* GetMatrix() { assert(type == MATRIX_4X4); return matrix; }
86 private:
87  void CopyShallow(const StdMeshMaterialShaderParameter& other);
88  void CopyDeep(const StdMeshMaterialShaderParameter& other);
89  void Move(StdMeshMaterialShaderParameter &&other);
90 
91  Type type{FLOAT4};
92 
93  union {
94  Auto a;
95  int i;
96  float f[4];
97  float* matrix; // 16 floats, row-major order
98  };
99 };
100 
102 {
103 public:
105 
106  void Load(StdMeshMaterialParserCtx& ctx);
107 
109 
110  std::vector<std::pair<StdCopyStrBuf, StdMeshMaterialShaderParameter> > NamedParameters;
111 private:
114 };
115 
120 };
121 
122 // Interface to load additional resources.
123 // Given a texture filename occuring in the
124 // material script, this should load the texture from wherever the material
125 // script is actually loaded, for example from a C4Group.
126 // Given a shader filename, this should load the shader text.
128 {
129 public:
130  virtual C4Surface* LoadTexture(const char* filename) = 0;
131  virtual StdStrBuf LoadShaderCode(const char* filename) = 0;
132  virtual void AddShaderSlices(C4Shader& shader, int ssc) = 0; // add default shader slices
133  virtual ~StdMeshMaterialLoader() = default;
134 };
135 
136 // This is just a container class to hold the shader code; the C4Shader
137 // objects are later created from that code by mixing them with the default
138 // slices.
140 {
141 public:
142  StdMeshMaterialShader(const char* filename, const char* name, const char* language, StdMeshMaterialShaderType /* type */, const char* code):
143  Filename(filename), Name(name), Language(language), Code(code)
144  {}
145 
146  const char* GetFilename() const { return Filename.getData(); }
147  const char* GetCode() const { return Code.getData(); }
148 
149 private:
150  StdCopyStrBuf Filename;
151  StdCopyStrBuf Name;
152  StdCopyStrBuf Language;
153  StdCopyStrBuf Code;
154 };
155 
157 {
158 public:
159  StdMeshMaterialProgram(const char* name, const StdMeshMaterialShader* fragment_shader, const StdMeshMaterialShader* vertex_shader, const StdMeshMaterialShader* geometry_shader);
160  bool AddParameterNames(const StdMeshMaterialShaderParameters& parameters); // returns true if some parameter names were not yet registered.
161 
162  bool IsCompiled() const { return Shader.Initialised(); }
163  bool Compile(StdMeshMaterialLoader& loader);
164 
165  const C4Shader* GetShader(int ssc) const;
166  int GetParameterIndex(const char* name) const;
167 
168  const StdMeshMaterialShader* GetFragmentShader() const { return FragmentShader; }
169  const StdMeshMaterialShader* GetVertexShader() const { return VertexShader; }
170  const StdMeshMaterialShader* GetGeometryShader() const { return GeometryShader; }
171 private:
172  bool CompileShader(StdMeshMaterialLoader& loader, C4Shader& shader, int ssc);
173 
174  // Human-readable program name
175  const StdCopyStrBuf Name;
176 
177  // Program components
178  const StdMeshMaterialShader* FragmentShader;
179  const StdMeshMaterialShader* VertexShader;
180  const StdMeshMaterialShader* GeometryShader;
181 
182  // Compiled shaders
183  C4Shader Shader;
184  C4Shader ShaderMod2;
185  C4Shader ShaderLight;
186  C4Shader ShaderLightMod2;
187 
188  // Filled as program references are encountered;
189  std::vector<StdCopyStrBuf> ParameterNames;
190 };
191 
193 {
194 public:
196  {
200  AM_Border
201  };
202 
204  {
209  };
210 
212  {
217  };
218 
220  {
236  };
237 
239  {
244  BOS_PlayerColor, // not specified in ogre, added in OpenClonk
245  BOS_Manual
246  };
247 
249  {
250  enum Type
251  {
259  };
260 
262  {
267  XF_SCALE_Y
268  };
269 
270  enum WaveType
271  {
277  };
278 
280 
281  union
282  {
283  struct { float X; float Y; } Scroll;
284  struct { float XSpeed; float YSpeed; } ScrollAnim;
285  struct { float Angle; } Rotate;
286  struct { float RevsPerSec; } RotateAnim;
287  struct { float X; float Y; } Scale;
288  struct { float M[16]; } Transform;
289  struct { XFormType XForm; WaveType Wave; float Base; float Frequency; float Phase; float Amplitude; } WaveXForm;
290  };
291 
292  double GetScrollX(double t) const { assert(TransformType == T_SCROLL_ANIM); return ScrollAnim.XSpeed * t; }
293  double GetScrollY(double t) const { assert(TransformType == T_SCROLL_ANIM); return ScrollAnim.YSpeed * t; }
294  double GetRotate(double t) const { assert(TransformType == T_ROTATE_ANIM); return fmod(RotateAnim.RevsPerSec * t, 1.0) * 360.0; }
295  double GetWaveXForm(double t) const;
296  };
297 
298  // Ref-counted texture. When a meterial inherits from one which contains
299  // a TextureUnit, then they will share the same C4TexRef.
300  class Tex
301  {
302  public:
303  Tex(C4Surface* Surface); // Takes ownership
304  ~Tex();
305 
306  unsigned int RefCount;
307 
308  // TODO: Note this cannot be C4Surface here, because C4Surface
309  // does not have a virtual destructor, so we couldn't delete it
310  // properly in that case. I am a bit annoyed that this
311  // currently requires a cross-ref to lib/texture. I think
312  // C4Surface should go away and the file loading/saving
313  // should be free functions instead. I also think the file
314  // loading/saving should be decoupled from the surfaces, so we
315  // can skip the surface here and simply use a C4TexRef. armin.
318  };
319 
320  // Simple wrapper which handles refcounting of Tex
321  class TexPtr
322  {
323  public:
324  TexPtr(C4Surface* Surface);
325  TexPtr(const TexPtr& other);
326  ~TexPtr();
327 
328  TexPtr& operator=(const TexPtr& other);
329 
331  };
332 
334 
335  void LoadTexture(StdMeshMaterialParserCtx& ctx, const char* texname);
336  void Load(StdMeshMaterialParserCtx& ctx);
337 
338  bool HasTexture() const { return !Textures.empty(); }
339  size_t GetNumTextures() const { return Textures.size(); }
340  const C4TexRef& GetTexture(unsigned int i) const { return Textures[i].pTex->Texture; }
341  bool HasFrameAnimation() const { return Duration > 0; }
342  bool HasTexCoordAnimation() const { return !Transformations.empty(); }
343 
345  float Duration{0.0f}; // Duration of texture animation, if any.
346 
348  float TexBorderColor[4];
349  FilteringType Filtering[3]; // min, max, mipmap
350 
353  float ColorOpManualFactor{0.0f};
356 
359  float AlphaOpManualFactor{0.0f};
362 
363  // Transformations to be applied to texture coordinates in order
364  std::vector<Transformation> Transformations;
365 
366 private:
367  std::vector<TexPtr> Textures;
368 };
369 
371 {
372 public:
374  {
377  CH_None
378  };
379 
381  {
392  };
393 
395  {
403  DF_Greater
404  };
405 
407  void Load(StdMeshMaterialParserCtx& ctx);
408 
409  bool IsOpaque() const { return SceneBlendFactors[1] == SB_Zero; }
410 
412  std::vector<StdMeshMaterialTextureUnit> TextureUnits;
413 
414  float Ambient[4];
415  float Diffuse[4];
416  float Specular[4];
417  float Emissive[4];
418  float Shininess;
419 
420  bool DepthCheck{true};
421  bool DepthWrite{true};
422 
428 
430  {
431  // This points into the StdMeshMatManager maps
433  // Parameters for this instance
435  };
436 
438  {
439  public:
440  ProgramInstance(const StdMeshMaterialProgram* program, const ShaderInstance* fragment_instance, const ShaderInstance* vertex_instance, const ShaderInstance* geometry_instance);
441 
442  // This points into the StdMeshMatManager map
444 
445  // Parameters for this instance
446  struct ParameterRef {
448  int UniformIndex; // Index into parameter table for this program
449  };
450 
451  std::vector<ParameterRef> Parameters;
452 
453  private:
454  void LoadParameterRefs(const ShaderInstance* instance);
455  };
456 
460 
461  // This is a shared_ptr and not a unique_ptr so that this class is
462  // copyable, so it can be inherited. However, when the inherited
463  // material is prepared, the ProgramInstance will be overwritten
464  // anyway, so in that sense a unique_ptr would be enough. We could
465  // change it and make this class only movable (not copyable), and
466  // provide inheritance by copying all other fields, and letting
467  // PrepareMaterial fill the program instance.
468  std::shared_ptr<ProgramInstance> Program;
469 
470 private:
471  void LoadShaderRef(StdMeshMaterialParserCtx& ctx, StdMeshMaterialShaderType type);
472 };
473 
475 {
476 public:
478 
479  void Load(StdMeshMaterialParserCtx& ctx);
480 
481  bool IsOpaque() const;
482 
484  std::vector<StdMeshMaterialPass> Passes;
485 
486  // Filled in by gfx implementation: Whether this technique is available on
487  // the hardware and gfx engine (DX/GL) we are running on
488  bool Available{false};
489 };
490 
492 {
493 public:
495  void Load(StdMeshMaterialParserCtx& ctx);
496 
497  bool IsOpaque() const { assert(BestTechniqueIndex >= 0); return Techniques[BestTechniqueIndex].IsOpaque(); }
498 
499  // Location the Material was loaded from
501  unsigned int Line{0};
502 
503  // Material name
505 
506  // Not currently used in Clonk, but don't fail when we see this in a
507  // Material script:
508  bool ReceiveShadows{true};
509 
510  // Available techniques
511  std::vector<StdMeshMaterialTechnique> Techniques;
512 
513  // Filled in by gfx implementation: Best technique to use
514  int BestTechniqueIndex{-1}; // Don't use a pointer into the Technique vector to save us from implementing a copyctor
515 };
516 
518 {
519  friend class StdMeshMaterialUpdate;
520 private:
521  typedef std::map<StdCopyStrBuf, StdMeshMaterial> MaterialMap;
522 
523 public:
526  SMM_ForceReload = 2
527  };
528 
529  class Iterator
530  {
531  friend class StdMeshMatManager;
532  public:
533  Iterator(const MaterialMap::iterator& iter): iter_(iter) {}
534  Iterator(const Iterator& iter) = default;
535 
536  Iterator operator=(const Iterator& iter) { iter_ = iter.iter_; return *this; }
537  Iterator& operator++() { ++iter_; return *this; }
538  bool operator==(const Iterator& other) const { return iter_ == other.iter_; }
539  bool operator!=(const Iterator& other) const { return iter_ != other.iter_; }
540 
541  const StdMeshMaterial& operator*() const { return iter_->second; }
542  const StdMeshMaterial* operator->() const { return &iter_->second; }
543  private:
544  MaterialMap::iterator iter_;
545  };
546 
547  // Remove all materials from manager. Make sure there is no StdMesh
548  // referencing any out there before calling this.
549  void Clear();
550 
551  // Parse a material script file, and add the materials to the manager.
552  // filename may be nullptr if the source is not a file. It will only be used
553  // for error messages.
554  // Throws StdMeshMaterialError.
555  // Returns a set of all loaded materials.
556  std::set<StdCopyStrBuf> Parse(const char* mat_script, const char* filename, StdMeshMaterialLoader& loader);
557 
558  // Get material by name. nullptr if there is no such material with this name.
559  const StdMeshMaterial* GetMaterial(const char* material_name) const;
560 
561  Iterator Begin() { return Iterator(Materials.begin()); }
562  Iterator End() { return Iterator(Materials.end()); }
563  void Remove(const StdStrBuf& name, class StdMeshMaterialUpdate* update);
564  Iterator Remove(const Iterator& iter, class StdMeshMaterialUpdate* update);
565 
566  const StdMeshMaterialShader* AddShader(const char* filename, const char* name, const char* language, StdMeshMaterialShaderType type, const char* text, uint32_t load_flags); // if load_flags & SMM_AcceptExisting, the function returns the existing shader, otherwise returns nullptr.
567  const StdMeshMaterialProgram* AddProgram(const char* name, StdMeshMaterialLoader& loader, const StdMeshMaterialPass::ShaderInstance& fragment_shader, const StdMeshMaterialPass::ShaderInstance& vertex_shader, const StdMeshMaterialPass::ShaderInstance& geometry_shader); // returns nullptr if shader code cannot be compiled
568 
569  const StdMeshMaterialShader* GetFragmentShader(const char* name) const;
570  const StdMeshMaterialShader* GetVertexShader(const char* name) const;
571  const StdMeshMaterialShader* GetGeometryShader(const char* name) const;
572 private:
573  MaterialMap Materials;
574 
575  // Shader code for custom shaders.
576  typedef std::map<StdCopyStrBuf, std::unique_ptr<StdMeshMaterialShader>> ShaderMap;
577  ShaderMap FragmentShaders;
578  ShaderMap VertexShaders;
579  ShaderMap GeometryShaders;
580 
581  // Linked programs
582  typedef std::map<std::tuple<const StdMeshMaterialShader*, const StdMeshMaterialShader*, const StdMeshMaterialShader*>, std::unique_ptr<StdMeshMaterialProgram> > ProgramMap;
583  ProgramMap Programs;
584 };
585 
587 
588 #endif
#define X(sdl, oc)
#define a
C4MaterialMap MaterialMap
Definition: C4Material.cpp:974
int32_t Angle(int32_t iX1, int32_t iY1, int32_t iX2, int32_t iY2, int32_t iPrec)
Definition: Standard.cpp:37
StdMeshMaterialShaderType
@ SMMS_VERTEX
@ SMMS_FRAGMENT
@ SMMS_GEOMETRY
StdMeshMatManager MeshMaterialManager
void Rotate(MatrixType &mat, float angle, float x, float y, float z)
Definition: StdMeshMath.h:209
void Scale(MatrixType &mat, float sx, float sy, float sz)
Definition: StdMeshMath.h:195
bool Initialised() const
Definition: C4Shader.h:106
Iterator(const MaterialMap::iterator &iter)
bool operator==(const Iterator &other) const
bool operator!=(const Iterator &other) const
Iterator operator=(const Iterator &iter)
const StdMeshMaterial & operator*() const
const StdMeshMaterial * operator->() const
Iterator(const Iterator &iter)=default
void Remove(const StdStrBuf &name, class StdMeshMaterialUpdate *update)
const StdMeshMaterialShader * GetGeometryShader(const char *name) const
std::set< StdCopyStrBuf > Parse(const char *mat_script, const char *filename, StdMeshMaterialLoader &loader)
const StdMeshMaterialShader * GetVertexShader(const char *name) const
const StdMeshMaterialProgram * AddProgram(const char *name, StdMeshMaterialLoader &loader, const StdMeshMaterialPass::ShaderInstance &fragment_shader, const StdMeshMaterialPass::ShaderInstance &vertex_shader, const StdMeshMaterialPass::ShaderInstance &geometry_shader)
const StdMeshMaterial * GetMaterial(const char *material_name) const
const StdMeshMaterialShader * GetFragmentShader(const char *name) const
const StdMeshMaterialShader * AddShader(const char *filename, const char *name, const char *language, StdMeshMaterialShaderType type, const char *text, uint32_t load_flags)
const char * what() const override
StdMeshMaterialError(const StdStrBuf &message, const char *file, unsigned int line)
~StdMeshMaterialError() override=default
void Load(StdMeshMaterialParserCtx &ctx)
StdCopyStrBuf FileName
unsigned int Line
StdCopyStrBuf Name
std::vector< StdMeshMaterialTechnique > Techniques
bool IsOpaque() const
virtual ~StdMeshMaterialLoader()=default
virtual void AddShaderSlices(C4Shader &shader, int ssc)=0
virtual StdStrBuf LoadShaderCode(const char *filename)=0
virtual C4Surface * LoadTexture(const char *filename)=0
const StdMeshMaterialProgram *const Program
ProgramInstance(const StdMeshMaterialProgram *program, const ShaderInstance *fragment_instance, const ShaderInstance *vertex_instance, const ShaderInstance *geometry_instance)
std::vector< ParameterRef > Parameters
const StdMeshMaterialShaderParameter * Parameter
const StdMeshMaterialShader * Shader
void Load(StdMeshMaterialParserCtx &ctx)
DepthFunctionType AlphaRejectionFunction
SceneBlendType SceneBlendFactors[2]
std::shared_ptr< ProgramInstance > Program
CullHardwareType CullHardware
StdMeshMaterialShaderParameters Parameters
ShaderInstance GeometryShader
ShaderInstance FragmentShader
std::vector< StdMeshMaterialTextureUnit > TextureUnits
ShaderInstance VertexShader
StdMeshMaterialProgram(const char *name, const StdMeshMaterialShader *fragment_shader, const StdMeshMaterialShader *vertex_shader, const StdMeshMaterialShader *geometry_shader)
const C4Shader * GetShader(int ssc) const
bool Compile(StdMeshMaterialLoader &loader)
bool AddParameterNames(const StdMeshMaterialShaderParameters &parameters)
int GetParameterIndex(const char *name) const
const StdMeshMaterialShader * GetVertexShader() const
const StdMeshMaterialShader * GetFragmentShader() const
const StdMeshMaterialShader * GetGeometryShader() const
StdMeshMaterialShader(const char *filename, const char *name, const char *language, StdMeshMaterialShaderType, const char *code)
const char * GetCode() const
const char * GetFilename() const
StdMeshMaterialShaderParameter & operator=(const StdMeshMaterialShaderParameter &other)
const float * GetMatrix() const
const float * GetFloatv() const
StdMeshMaterialShaderParameter & AddParameter(const char *name, StdMeshMaterialShaderParameter::Type type)
std::vector< std::pair< StdCopyStrBuf, StdMeshMaterialShaderParameter > > NamedParameters
void Load(StdMeshMaterialParserCtx &ctx)
std::vector< StdMeshMaterialPass > Passes
void Load(StdMeshMaterialParserCtx &ctx)
TexPtr & operator=(const TexPtr &other)
BlendOpSourceType ColorOpSources[2]
const C4TexRef & GetTexture(unsigned int i) const
void LoadTexture(StdMeshMaterialParserCtx &ctx, const char *texname)
TexAddressModeType TexAddressMode
BlendOpSourceType AlphaOpSources[2]
void Load(StdMeshMaterialParserCtx &ctx)
std::vector< Transformation > Transformations
const char * getData() const
Definition: StdBuf.h:442