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 28 class StdMeshMaterialParserCtx; 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 what()36 const char* what() const throw() override { return Buf.getData(); } 37 38 protected: 39 StdCopyStrBuf Buf; 40 }; 41 42 class StdMeshMaterialShaderParameter 43 { 44 public: 45 enum Type { 46 AUTO, 47 AUTO_TEXTURE_MATRIX, // Texture matrix for the i-th texture 48 INT, 49 FLOAT, 50 FLOAT2, 51 FLOAT3, 52 FLOAT4, 53 MATRIX_4X4 54 }; 55 56 enum Auto { 57 // TODO: OGRE auto values 58 AUTO_DUMMY 59 }; 60 61 StdMeshMaterialShaderParameter(); // type=FLOAT, value uninitialized 62 StdMeshMaterialShaderParameter(Type type); // value uninitialized 63 StdMeshMaterialShaderParameter(const StdMeshMaterialShaderParameter& other); 64 StdMeshMaterialShaderParameter(StdMeshMaterialShaderParameter &&other); 65 ~StdMeshMaterialShaderParameter(); 66 67 StdMeshMaterialShaderParameter& operator=(const StdMeshMaterialShaderParameter& other); 68 StdMeshMaterialShaderParameter& operator=(StdMeshMaterialShaderParameter &&other); 69 GetType()70 Type GetType() const { return type; } 71 void SetType(Type type); // changes type, new value is uninitialized 72 73 // Getters GetAuto()74 Auto GetAuto() const { assert(type == AUTO); return a; } GetInt()75 int GetInt() const { assert(type == INT || type == AUTO_TEXTURE_MATRIX); return i; } GetFloat()76 float GetFloat() const { assert(type == FLOAT); return f[0]; } GetFloatv()77 const float* GetFloatv() const { assert(type == FLOAT2 || type == FLOAT3 || type == FLOAT4); return f; } GetMatrix()78 const float* GetMatrix() const { assert(type == MATRIX_4X4); return matrix; } 79 80 // Setters GetAuto()81 Auto& GetAuto() { assert(type == AUTO); return a; } GetInt()82 int& GetInt() { assert(type == INT || type == AUTO_TEXTURE_MATRIX); return i; } GetFloat()83 float& GetFloat() { assert(type == FLOAT); return f[0]; } GetFloatv()84 float* GetFloatv() { assert(type == FLOAT2 || type == FLOAT3 || type == FLOAT4); return f; } GetMatrix()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 101 class StdMeshMaterialShaderParameters 102 { 103 public: 104 StdMeshMaterialShaderParameters(); 105 106 void Load(StdMeshMaterialParserCtx& ctx); 107 108 StdMeshMaterialShaderParameter& AddParameter(const char* name, StdMeshMaterialShaderParameter::Type type); 109 110 std::vector<std::pair<StdCopyStrBuf, StdMeshMaterialShaderParameter> > NamedParameters; 111 private: 112 StdMeshMaterialShaderParameter LoadConstParameter(StdMeshMaterialParserCtx& ctx); 113 StdMeshMaterialShaderParameter LoadAutoParameter(StdMeshMaterialParserCtx& ctx); 114 }; 115 116 enum StdMeshMaterialShaderType { 117 SMMS_FRAGMENT, 118 SMMS_VERTEX, 119 SMMS_GEOMETRY 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. 127 class StdMeshMaterialLoader 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. 139 class StdMeshMaterialShader 140 { 141 public: StdMeshMaterialShader(const char * filename,const char * name,const char * language,StdMeshMaterialShaderType,const char * code)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 GetFilename()146 const char* GetFilename() const { return Filename.getData(); } GetCode()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 156 class StdMeshMaterialProgram 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 IsCompiled()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 GetFragmentShader()168 const StdMeshMaterialShader* GetFragmentShader() const { return FragmentShader; } GetVertexShader()169 const StdMeshMaterialShader* GetVertexShader() const { return VertexShader; } GetGeometryShader()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 192 class StdMeshMaterialTextureUnit 193 { 194 public: 195 enum TexAddressModeType 196 { 197 AM_Wrap, 198 AM_Clamp, 199 AM_Mirror, 200 AM_Border 201 }; 202 203 enum FilteringType 204 { 205 F_None, 206 F_Point, 207 F_Linear, 208 F_Anisotropic 209 }; 210 211 enum BlendOpType 212 { 213 BO_Replace, 214 BO_Add, 215 BO_Modulate, 216 BO_AlphaBlend 217 }; 218 219 enum BlendOpExType 220 { 221 BOX_Source1, 222 BOX_Source2, 223 BOX_Modulate, 224 BOX_ModulateX2, 225 BOX_ModulateX4, 226 BOX_Add, 227 BOX_AddSigned, 228 BOX_AddSmooth, 229 BOX_Subtract, 230 BOX_BlendDiffuseAlpha, 231 BOX_BlendTextureAlpha, 232 BOX_BlendCurrentAlpha, 233 BOX_BlendManual, 234 BOX_Dotproduct, 235 BOX_BlendDiffuseColor 236 }; 237 238 enum BlendOpSourceType 239 { 240 BOS_Current, 241 BOS_Texture, 242 BOS_Diffuse, 243 BOS_Specular, 244 BOS_PlayerColor, // not specified in ogre, added in OpenClonk 245 BOS_Manual 246 }; 247 248 struct Transformation 249 { 250 enum Type 251 { 252 T_SCROLL, 253 T_SCROLL_ANIM, 254 T_ROTATE, 255 T_ROTATE_ANIM, 256 T_SCALE, 257 T_TRANSFORM, 258 T_WAVE_XFORM 259 }; 260 261 enum XFormType 262 { 263 XF_SCROLL_X, 264 XF_SCROLL_Y, 265 XF_ROTATE, 266 XF_SCALE_X, 267 XF_SCALE_Y 268 }; 269 270 enum WaveType 271 { 272 W_SINE, 273 W_TRIANGLE, 274 W_SQUARE, 275 W_SAWTOOTH, 276 W_INVERSE_SAWTOOTH 277 }; 278 279 Type TransformType; 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 GetScrollXTransformation292 double GetScrollX(double t) const { assert(TransformType == T_SCROLL_ANIM); return ScrollAnim.XSpeed * t; } GetScrollYTransformation293 double GetScrollY(double t) const { assert(TransformType == T_SCROLL_ANIM); return ScrollAnim.YSpeed * t; } GetRotateTransformation294 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. 316 C4Surface* Surf; 317 C4TexRef& Texture; 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 330 Tex* pTex; 331 }; 332 333 StdMeshMaterialTextureUnit(); 334 335 void LoadTexture(StdMeshMaterialParserCtx& ctx, const char* texname); 336 void Load(StdMeshMaterialParserCtx& ctx); 337 HasTexture()338 bool HasTexture() const { return !Textures.empty(); } GetNumTextures()339 size_t GetNumTextures() const { return Textures.size(); } GetTexture(unsigned int i)340 const C4TexRef& GetTexture(unsigned int i) const { return Textures[i].pTex->Texture; } HasFrameAnimation()341 bool HasFrameAnimation() const { return Duration > 0; } HasTexCoordAnimation()342 bool HasTexCoordAnimation() const { return !Transformations.empty(); } 343 344 StdCopyStrBuf Name; 345 float Duration{0.0f}; // Duration of texture animation, if any. 346 347 TexAddressModeType TexAddressMode{AM_Wrap}; 348 float TexBorderColor[4]; 349 FilteringType Filtering[3]; // min, max, mipmap 350 351 BlendOpExType ColorOpEx{BOX_Modulate}; 352 BlendOpSourceType ColorOpSources[2]; 353 float ColorOpManualFactor{0.0f}; 354 float ColorOpManualColor1[3]; 355 float ColorOpManualColor2[3]; 356 357 BlendOpExType AlphaOpEx{BOX_Modulate}; 358 BlendOpSourceType AlphaOpSources[2]; 359 float AlphaOpManualFactor{0.0f}; 360 float AlphaOpManualAlpha1; 361 float AlphaOpManualAlpha2; 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 370 class StdMeshMaterialPass 371 { 372 public: 373 enum CullHardwareType 374 { 375 CH_Clockwise, 376 CH_CounterClockwise, 377 CH_None 378 }; 379 380 enum SceneBlendType 381 { 382 SB_One, 383 SB_Zero, 384 SB_DestColor, 385 SB_SrcColor, 386 SB_OneMinusDestColor, 387 SB_OneMinusSrcColor, 388 SB_DestAlpha, 389 SB_SrcAlpha, 390 SB_OneMinusDestAlpha, 391 SB_OneMinusSrcAlpha 392 }; 393 394 enum DepthFunctionType 395 { 396 DF_AlwaysFail, 397 DF_AlwaysPass, 398 DF_Less, 399 DF_LessEqual, 400 DF_Equal, 401 DF_NotEqual, 402 DF_GreaterEqual, 403 DF_Greater 404 }; 405 406 StdMeshMaterialPass(); 407 void Load(StdMeshMaterialParserCtx& ctx); 408 IsOpaque()409 bool IsOpaque() const { return SceneBlendFactors[1] == SB_Zero; } 410 411 StdCopyStrBuf Name; 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 423 CullHardwareType CullHardware{CH_Clockwise}; 424 SceneBlendType SceneBlendFactors[2]; 425 DepthFunctionType AlphaRejectionFunction; 426 float AlphaRejectionValue; 427 bool AlphaToCoverage; 428 429 struct ShaderInstance 430 { 431 // This points into the StdMeshMatManager maps 432 const StdMeshMaterialShader* Shader; 433 // Parameters for this instance 434 StdMeshMaterialShaderParameters Parameters; 435 }; 436 437 class ProgramInstance 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 443 const StdMeshMaterialProgram* const Program; 444 445 // Parameters for this instance 446 struct ParameterRef { 447 const StdMeshMaterialShaderParameter* Parameter; 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 457 ShaderInstance FragmentShader; 458 ShaderInstance VertexShader; 459 ShaderInstance GeometryShader; 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 474 class StdMeshMaterialTechnique 475 { 476 public: 477 StdMeshMaterialTechnique(); 478 479 void Load(StdMeshMaterialParserCtx& ctx); 480 481 bool IsOpaque() const; 482 483 StdCopyStrBuf Name; 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 491 class StdMeshMaterial 492 { 493 public: 494 StdMeshMaterial(); 495 void Load(StdMeshMaterialParserCtx& ctx); 496 IsOpaque()497 bool IsOpaque() const { assert(BestTechniqueIndex >= 0); return Techniques[BestTechniqueIndex].IsOpaque(); } 498 499 // Location the Material was loaded from 500 StdCopyStrBuf FileName; 501 unsigned int Line{0}; 502 503 // Material name 504 StdCopyStrBuf 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 517 class StdMeshMatManager 518 { 519 friend class StdMeshMaterialUpdate; 520 private: 521 typedef std::map<StdCopyStrBuf, StdMeshMaterial> MaterialMap; 522 523 public: 524 enum ShaderLoadFlag { 525 SMM_AcceptExisting = 1, 526 SMM_ForceReload = 2 527 }; 528 529 class Iterator 530 { 531 friend class StdMeshMatManager; 532 public: Iterator(const MaterialMap::iterator & iter)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 Begin()561 Iterator Begin() { return Iterator(Materials.begin()); } End()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 586 extern StdMeshMatManager MeshMaterialManager; 587 588 #endif 589