TDME2  1.9.200
Texture.cpp
Go to the documentation of this file.
1 #include <string>
2 #include <vector>
3 
4 #include <tdme/tdme.h>
5 #include <tdme/engine/Texture.h>
12 #include <tdme/utilities/Console.h>
13 
14 using std::string;
15 using std::to_string;
16 using std::vector;
17 
19 
27 
28 ByteBuffer Texture::getRGBTextureData(TextureFormat format, const ByteBuffer& textureData) {
29  // do we already have the requested rgb format?
31  if (format == rgbFormat) {
32  // yup, done
33  return textureData;
34  }
35 
36  // no?, convert it
37  switch(format) {
38  case TEXTUREFORMAT_RGB:
39  {
40  // input texture parameters
41  auto textureBytesPerPixel = 3;
42  // generated rgb raw data
43  auto rgbTextureDataBytesPerPixel = getRGBDepthBitsPerPixel() / 8;
44  auto rgbTextureData = ByteBuffer(textureWidth * textureHeight * rgbTextureDataBytesPerPixel);
45  for (auto y = 0; y < textureHeight; y++) {
46  for (auto x = 0; x < textureWidth; x++) {
47  auto offset = y * textureWidth * textureBytesPerPixel + x * textureBytesPerPixel;
48  auto red = textureData.get(offset + 0);
49  auto green = textureData.get(offset + 1);
50  auto blue = textureData.get(offset + 2);
51  auto alpha = 0xff;
52  //
53  rgbTextureData.put(red);
54  rgbTextureData.put(green);
55  rgbTextureData.put(blue);
56  if (rgbTextureDataBytesPerPixel == 4) rgbTextureData.put(0xff);
57  }
58  }
59  //
60  return rgbTextureData;
61  }
62  case TEXTUREFORMAT_RGBA:
63  {
64  // input texture parameters
65  auto textureBytesPerPixel = 4;
66  // generated rgb raw data
67  auto rgbTextureDataBytesPerPixel = getRGBDepthBitsPerPixel() / 8;
68  auto rgbTextureData = ByteBuffer(textureWidth * textureHeight * rgbTextureDataBytesPerPixel);
69  for (auto y = 0; y < textureHeight; y++) {
70  for (auto x = 0; x < textureWidth; x++) {
71  auto offset = y * textureWidth * textureBytesPerPixel + x * textureBytesPerPixel;
72  auto red = textureData.get(offset + 0);
73  auto green = textureData.get(offset + 1);
74  auto blue = textureData.get(offset + 2);
75  auto alpha = textureData.get(offset + 3);
76  //
77  rgbTextureData.put(red);
78  rgbTextureData.put(green);
79  rgbTextureData.put(blue);
80  if (rgbTextureDataBytesPerPixel == 4) rgbTextureData.put(alpha);
81  }
82  }
83  //
84  return rgbTextureData;
85  }
87  {
88  // generated rgb raw data
89  auto rgbTextureDataBytesPerPixel = getRGBDepthBitsPerPixel() / 8;
90  auto rgbTextureData = ByteBuffer(textureWidth * textureHeight * rgbTextureDataBytesPerPixel);
91 
92  //
93  PNGTextureReader::read(*textureData.getBufferVector(), rgbTextureData);
94 
95  //
96  return rgbTextureData;
97  }
99  {
100  // generated rgb raw data
101  auto rgbTextureDataBytesPerPixel = getRGBDepthBitsPerPixel() / 8;
102  auto rgbTextureData = ByteBuffer(textureWidth * textureHeight * rgbTextureDataBytesPerPixel);
103 
104  //
105  PNGTextureReader::read(*textureData.getBufferVector(), rgbTextureData);
106 
107  //
108  return rgbTextureData;
109  }
111  {
112  // generated rgb raw data
113  auto rgbTextureDataBytesPerPixel = getRGBDepthBitsPerPixel() / 8;
114  auto rgbTextureData = ByteBuffer(textureWidth * textureHeight * rgbTextureDataBytesPerPixel);
115 
116  //
117  BC7TextureReader::read(textureWidth, textureHeight, rgbTextureDataBytesPerPixel, *textureData.getBufferVector(), rgbTextureData);
118 
119  //
120  return rgbTextureData;
121  }
123  {
124  // generated rgb raw data
125  auto rgbTextureDataBytesPerPixel = getRGBDepthBitsPerPixel() / 8;
126  auto rgbTextureData = ByteBuffer(textureWidth * textureHeight * rgbTextureDataBytesPerPixel);
127 
128  //
129  BC7TextureReader::read(textureWidth, textureHeight, rgbTextureDataBytesPerPixel, *textureData.getBufferVector(), rgbTextureData);
130 
131  //
132  return rgbTextureData;
133  }
134  }
135 
136  //
137  return ByteBuffer();
138 }
139 
141  // do we already have the requested bc7 format?
143  if (format == bc7Format) {
144  // yup, done
145  return textureData;
146  }
147 
148  //
149  auto rgbaTextureData = getRGBTextureData();
150  auto rgbaTextureDataBytesPerPixel = getRGBDepthBitsPerPixel() / 8;
151 
152  // no?, convert it
153  vector<uint8_t> bc7Data;
154  BC7TextureWriter::write(textureWidth, textureHeight, rgbaTextureDataBytesPerPixel, rgbaTextureData, bc7Data);
155 
156  //
157  return ByteBuffer(bc7Data);
158 }
159 
160 void Texture::setTextureData(TextureFormat format, const ByteBuffer& textureData) {
161  if (this->format == format) {
162  this->textureData = textureData;
163  } else {
164  switch(this->format) {
165  case TEXTUREFORMAT_RGB:
166  {
167  //
168  auto textureSize = textureWidth * textureHeight * 3;
169  this->textureData = ByteBuffer(textureSize);
170  // read rgb raw data
171  auto rgbTextureData = getRGBTextureData(format, textureData);
172  auto rgbTextureDataBytesPerPixel = getRGBDepthBitsPerPixel() / 8;
173  for (auto y = 0; y < textureHeight; y++) {
174  for (auto x = 0; x < textureWidth; x++) {
175  auto offset = y * textureWidth * rgbTextureDataBytesPerPixel + x * rgbTextureDataBytesPerPixel;
176  auto red = rgbTextureData.get(offset + 0);
177  auto green = rgbTextureData.get(offset + 1);
178  auto blue = rgbTextureData.get(offset + 2);
179  auto alpha = rgbTextureDataBytesPerPixel == 4?rgbTextureData.get(offset + 3):0xff;
180  //
181  this->textureData.put(red);
182  this->textureData.put(green);
183  this->textureData.put(blue);
184  }
185  }
186  break;
187  }
188  case TEXTUREFORMAT_RGBA:
189  {
190  //
191  auto textureSize = textureWidth * textureHeight * 4;
192  this->textureData = ByteBuffer(textureSize);
193  // read rgb raw data
194  auto rgbTextureData = getRGBTextureData(format, textureData);
195  auto rgbTextureDataBytesPerPixel = getRGBDepthBitsPerPixel() / 8;
196  for (auto y = 0; y < textureHeight; y++) {
197  for (auto x = 0; x < textureWidth; x++) {
198  auto offset = y * textureWidth * rgbTextureDataBytesPerPixel + x * rgbTextureDataBytesPerPixel;
199  auto red = rgbTextureData.get(offset + 0);
200  auto green = rgbTextureData.get(offset + 1);
201  auto blue = rgbTextureData.get(offset + 2);
202  auto alpha = rgbTextureDataBytesPerPixel == 4?rgbTextureData.get(offset + 3):0xff;
203  //
204  this->textureData.put(red);
205  this->textureData.put(green);
206  this->textureData.put(blue);
207  this->textureData.put(alpha);
208  }
209  }
210  break;
211  }
213  {
214  // read rgb raw data
215  auto rgbTextureData = getRGBTextureData(format, textureData);
216  //
217  vector<uint8_t> pngData;
218  PNGTextureWriter::write(textureWidth, textureHeight, getRGBDepthBitsPerPixel() / 8, rgbTextureData, pngData, true, false);
219  //
220  this->textureData = ByteBuffer(pngData);
221  //
222  break;
223  }
225  {
226  // read rgb raw data
227  auto rgbTextureData = getRGBTextureData(format, textureData);
228  //
229  vector<uint8_t> pngData;
230  PNGTextureWriter::write(textureWidth, textureHeight, getRGBDepthBitsPerPixel() / 8, rgbTextureData, pngData, false, false);
231  //
232  this->textureData = ByteBuffer(pngData);
233  //
234  break;
235  }
236  }
237  }
238 }
239 
240 ByteBuffer Texture::generateMipMap(int textureWidth, int textureHeight, int bytesPerPixel, const ByteBuffer& textureTextureData) {
241  auto generatedTextureWidth = textureWidth / 2;
242  auto generatedTextureHeight = textureHeight / 2;
243  auto generatedTextureByteBuffer = ByteBuffer(generatedTextureWidth * generatedTextureHeight * bytesPerPixel);
244  auto materialTextureWidth = textureWidth / atlasSize;
245  auto materialTextureHeight = textureHeight / atlasSize;
246  auto materialTextureBytesPerPixel = bytesPerPixel;
247  for (auto y = 0; y < generatedTextureHeight; y++)
248  for (auto x = 0; x < generatedTextureWidth; x++) {
249  auto atlasTextureIdxX = (x * 2) / materialTextureWidth;
250  auto atlasTextureIdxY = (y * 2) / materialTextureHeight;
251  auto materialTextureX = (x * 2) - (atlasTextureIdxX * materialTextureWidth);
252  auto materialTextureY = (y * 2) - (atlasTextureIdxY * materialTextureHeight);
253  auto materialTextureXFloat = static_cast<float>(materialTextureX) / static_cast<float>(materialTextureWidth);
254  auto materialTextureYFloat = static_cast<float>(materialTextureY) / static_cast<float>(materialTextureHeight);
255  {
256  auto materialSamples = 0;
257  auto materialTextureXInt = static_cast<int>(materialTextureXFloat * static_cast<float>(materialTextureWidth));
258  auto materialTextureYInt = static_cast<int>(materialTextureYFloat * static_cast<float>(materialTextureHeight));
259  auto materialPixelR = 0;
260  auto materialPixelG = 0;
261  auto materialPixelB = 0;
262  auto materialPixelA = 0;
263  for (auto y = -1; y <= 1; y++)
264  for (auto x = -1; x <= 1; x++)
265  if ((Math::abs(x) == 1 && Math::abs(y) == 1) == false &&
266  materialTextureXInt + x >= 0 && materialTextureXInt + x < materialTextureWidth &&
267  materialTextureYInt + y >= 0 && materialTextureYInt + y < materialTextureHeight) {
268  auto materialTexturePixelOffset =
269  (atlasTextureIdxY * materialTextureHeight + materialTextureYInt + y) * textureWidth * materialTextureBytesPerPixel +
270  (atlasTextureIdxX * materialTextureWidth + materialTextureXInt + x) * materialTextureBytesPerPixel;
271  materialPixelR+= textureTextureData.get(materialTexturePixelOffset + 0);
272  materialPixelG+= textureTextureData.get(materialTexturePixelOffset + 1);
273  materialPixelB+= textureTextureData.get(materialTexturePixelOffset + 2);
274  materialPixelA+= materialTextureBytesPerPixel == 4?textureTextureData.get(materialTexturePixelOffset + 3):0xff;
275  materialSamples++;
276  }
277  generatedTextureByteBuffer.put(materialPixelR / materialSamples);
278  generatedTextureByteBuffer.put(materialPixelG / materialSamples);
279  generatedTextureByteBuffer.put(materialPixelB / materialSamples);
280  if (bytesPerPixel == 4) generatedTextureByteBuffer.put(materialPixelA / materialSamples);
281  }
282  }
283  return generatedTextureByteBuffer;
284 }
285 
286 const vector<Texture::MipMapTexture>& Texture::getMipMapTextures(bool bc7Encoded) {
287  // do we have any mip maps stored already?
288  if (mipMapTextures.empty() == false) {
289  // for now we only support stored BC7 mip maps and only return those if bc7 encodeding was requested
290  // TODO: maybe conversion from BC7 to RGB is required here conditionally, but can also be that decoding BC7 is slower than generating new mip maps
291  auto mipMapFormatBC7 = mipMapTextures[0].format == TEXTUREFORMAT_RGB_BC7 || mipMapTextures[0].format == TEXTUREFORMAT_RGBA_BC7;
292  if (mipMapFormatBC7 == true) {
293  if (bc7Encoded == true) return mipMapTextures;
294  } else {
295  if (bc7Encoded == false) return mipMapTextures;
296  }
297  //
298  mipMapTextures.clear();
299  }
300 
301  //
302  auto mipLevels = getMipLevels();
303  auto mipMapTextureWidth = textureWidth;
304  auto mipMapTextureHeight = textureHeight;
305  auto textureTextureData = getRGBTextureData();
306  auto textureBytePerPixel = getRGBDepthBitsPerPixel() / 8;
307  for (auto i = 1; i < mipLevels; i++) {
308  textureTextureData = generateMipMap(mipMapTextureWidth, mipMapTextureHeight, textureBytePerPixel, textureTextureData);
309  //
310  mipMapTextureWidth/= 2;
311  mipMapTextureHeight/= 2;
312  //
313  if (bc7Encoded == true) {
314  vector<uint8_t> bc7Data;
315  BC7TextureWriter::write(mipMapTextureWidth, mipMapTextureHeight, textureBytePerPixel, textureTextureData, bc7Data);
316  //
317  mipMapTextures.emplace_back(
319  mipMapTextureWidth,
320  mipMapTextureHeight,
321  ByteBuffer(bc7Data)
322  );
323  } else {
324  //
325  mipMapTextures.emplace_back(
327  mipMapTextureWidth,
328  mipMapTextureHeight,
329  textureTextureData
330  );
331  }
332  }
333  return mipMapTextures;
334 }
335 
337  TextureReader::removeFromCache(this);
338 }
Texture entity.
Definition: Texture.h:24
static TextureFormat getBC7FormatByPixelBitsPerPixel(int bpp)
Return BC7 RGB/A texture format by bits per pixel.
Definition: Texture.h:98
const vector< MipMapTexture > & getMipMapTextures(bool bc7Encoded)
Get mip map textures.
Definition: Texture.cpp:286
uint16_t atlasSize
Definition: Texture.h:445
static TextureFormat getRGBFormatByPixelBitsPerPixel(int bpp)
Return RGB/A texture format by bits per pixel.
Definition: Texture.h:72
ByteBuffer generateMipMap(int textureWidth, int textureHeight, int bytesPerPixel, const ByteBuffer &textureTextureData)
Generate mip map texture.
Definition: Texture.cpp:240
ByteBuffer getRGBTextureData()
Definition: Texture.h:253
uint8_t getRGBDepthBitsPerPixel() const
Definition: Texture.h:186
virtual void onDelete() override
Callback method to be overridden, will be called if object will be deleted.
Definition: Texture.cpp:336
uint16_t textureWidth
Definition: Texture.h:437
ByteBuffer getBC7TextureData()
Definition: Texture.cpp:140
vector< MipMapTexture > mipMapTextures
Definition: Texture.h:446
void setTextureData(TextureFormat format, const ByteBuffer &textureData)
Set RGB(A) texture data.
Definition: Texture.cpp:160
ByteBuffer textureData
Definition: Texture.h:438
uint16_t textureHeight
Definition: Texture.h:436
TextureFormat format
Definition: Texture.h:433
uint8_t get(int64_t position) const
Definition: Buffer.h:107
const vector< uint8_t > * getBufferVector() const
Definition: Buffer.h:136
Buffer * put(uint8_t value)
Put value into buffer.
Definition: Buffer.h:115
Byte buffer class.
Definition: ByteBuffer.h:27
Console class.
Definition: Console.h:29