1 /**
2  * Defines the Material and Texture classes.
3  */
4 module dash.components.material;
5 import dash.core, dash.components, dash.graphics, dash.utility;
6 
7 import yaml;
8 import derelict.opengl3.gl3, derelict.freeimage.freeimage;
9 import std.variant, std.conv, std..string;
10 
11 mixin( registerComponents!() );
12 
13 /**
14  * A collection of textures that serve different purposes in the rendering pipeline.
15  */
16 final class MaterialAsset : Asset
17 {
18 public:
19     /// The name of the material.
20     @rename( "Name" )
21     string name;
22     /// The diffuse (or color) map.
23     @rename( "Diffuse" ) @asArray @optional
24     Texture diffuse;
25     /// The normal map, which specifies which way a face is pointing at a given pixel.
26     @rename( "Normal" ) @asArray @optional
27     Texture normal;
28     /// The specular map, which specifies how shiny a given point is.
29     @rename( "Specular" ) @asArray @optional
30     Texture specular;
31 
32     /**
33      * Default constructor, makes sure everything is initialized to default.
34      */
35     this( string name = "" )
36     {
37         super( internalResource );
38         diffuse = specular = defaultTex;
39         normal = defaultNormal;
40         this.name = name;
41     }
42 
43     /**
44      * Duplicate the material.
45      */
46     MaterialAsset clone()
47     {
48         auto mat = new MaterialAsset;
49         mat.diffuse = diffuse;
50         mat.normal = normal;
51         mat.specular = specular;
52         mat.name = name;
53         return mat;
54     }
55 
56     /**
57      * Shuts down the material, making sure all references are released.
58      */
59     override void shutdown()
60     {
61         diffuse = specular = normal = null;
62     }
63 }
64 
65 /**
66  * A reference to a material.
67  */
68 final class Material : AssetRef!MaterialAsset
69 {
70     alias asset this;
71 
72     this() { }
73     this( MaterialAsset ass )
74     {
75         super( ass );
76     }
77 
78     override void initialize()
79     {
80         super.initialize();
81 
82         // All materials should be unique.
83         if( asset )
84             asset = asset.clone();
85 
86         asset.diffuse.initialize();
87         asset.normal.initialize();
88         asset.specular.initialize();
89     }
90 }
91 
92 /**
93  * TODO
94  */
95 class TextureAsset : Asset
96 {
97 protected:
98     uint _width = 1;
99     uint _height = 1;
100     uint _glID;
101 
102     /**
103      * TODO
104      *
105      * Params:
106      */
107     this( ubyte* buffer, Resource filePath )
108     {
109         super( filePath );
110         glGenTextures( 1, &_glID );
111         glBindTexture( GL_TEXTURE_2D, glID );
112         updateBuffer( buffer );
113     }
114 
115     /**
116      * TODO
117      *
118      * Params:
119      */
120     void updateBuffer( const ubyte* buffer )
121     {
122         // Set texture to update
123         glBindTexture( GL_TEXTURE_2D, glID );
124 
125         // Update texture
126         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
127         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
128         glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, _width, _height, 0, GL_BGRA, GL_UNSIGNED_BYTE, buffer );
129         glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
130     }
131 
132 public:
133     /// TODO
134     mixin( Property!_width );
135     /// TODO
136     mixin( Property!_height );
137     /// TODO
138     mixin( Property!_glID );
139 
140     /**
141      * TODO
142      *
143      * Params:
144      */
145     this( Resource filePath )
146     {
147         auto imageData = loadFreeImage( filePath.fullPath );
148 
149         this( cast(ubyte*)FreeImage_GetBits( imageData ), filePath );
150 
151         FreeImage_Unload( imageData );
152     }
153 
154     /**
155      * Refresh the asset.
156      */
157     override void refresh()
158     {
159         auto imageData = loadFreeImage( resource.fullPath );
160 
161         updateBuffer( cast(ubyte*)FreeImage_GetBits( imageData ) );
162 
163         FreeImage_Unload( imageData );
164     }
165 
166     /**
167      * TODO
168      */
169     override void shutdown()
170     {
171         glBindTexture( GL_TEXTURE_2D, 0 );
172         glDeleteBuffers( 1, &_glID );
173     }
174 
175 private:
176     auto loadFreeImage( string filePath )
177     {
178         filePath ~= '\0';
179         auto imageData = FreeImage_ConvertTo32Bits( FreeImage_Load( FreeImage_GetFileType( filePath.ptr, 0 ), filePath.ptr, 0 ) );
180 
181         width = FreeImage_GetWidth( imageData );
182         height = FreeImage_GetHeight( imageData );
183 
184         return imageData;
185     }
186 }
187 
188 /**
189  * A reference to a texture.
190  */
191 class Texture : AssetRef!TextureAsset
192 {
193     alias asset this;
194 
195     this() { }
196     this( TextureAsset ass )
197     {
198         super( ass );
199     }
200 }
201 
202 /**
203  * A default black texture.
204  */
205 @property Texture defaultTex()
206 {
207     static Texture def;
208 
209     if( !def )
210         def = new Texture( new TextureAsset( [cast(ubyte)0, cast(ubyte)0, cast(ubyte)0, cast(ubyte)255].ptr, internalResource ) );
211 
212     return def;
213 }
214 
215 /**
216  * A default gray texture
217  */
218 @property Texture defaultNormal()
219 {
220     static Texture def;
221 
222     if( !def )
223         def = new Texture( new TextureAsset( [cast(ubyte)255, cast(ubyte)127, cast(ubyte)127, cast(ubyte)255].ptr, internalResource ) );
224 
225     return def;
226 }