1//************************************ bs::framework - Copyright 2018 Marko Pintera **************************************//
2//*********** Licensed under the MIT license. See LICENSE.md for full terms. This notice is not to be removed. ***********//
3#include "Particles/BsVectorField.h"
4#include "Private/RTTI/BsVectorFieldRTTI.h"
5#include "Image/BsTexture.h"
6#include "Resources/BsResources.h"
7#include "FileSystem/BsFileSystem.h"
8#include "FileSystem/BsDataStream.h"
9
10namespace bs
11{
12 namespace detail
13 {
14 template class TVectorField <false>;
15 template class TVectorField <true>;
16 }
17
18 VectorField::VectorField(const VECTOR_FIELD_DESC& desc, const Vector<Vector3>& values)
19 :TVectorField(desc)
20 {
21 if(mDesc.countX == 0 || mDesc.countY == 0 || mDesc.countZ == 0)
22 LOGWRN("Vector field count cannot be zero.");
23
24 mDesc.countX = std::max(1U, mDesc.countX);
25 mDesc.countY = std::max(1U, mDesc.countY);
26 mDesc.countZ = std::max(1U, mDesc.countZ);
27
28 const UINT32 count = mDesc.countX * mDesc.countY * mDesc.countZ;
29 if(count != (UINT32)values.size())
30 {
31 LOGWRN(StringUtil::format("Number of values provided to the vector field does not match the expected number. \
32 Expected: {0}. Got: {1}.", count, values.size()));
33 }
34
35 const UINT32 valuesToCopy = std::min(count, (UINT32)values.size());
36
37 const SPtr<PixelData> pixelData = PixelData::create(mDesc.countX, mDesc.countY, mDesc.countZ, PF_RGBA16F);
38
39 const UINT32 pixelSize = PixelUtil::getNumElemBytes(PF_RGBA16F);
40 UINT8* data = pixelData->getData();
41 for(UINT32 z = 0; z < (UINT32)mDesc.countZ; z++)
42 {
43 const UINT32 zArrayIdx = z * mDesc.countY * mDesc.countX;
44 const UINT32 zDataIdx = z * pixelData->getSlicePitch() * pixelSize;
45
46 for(UINT32 y = 0; y < (UINT32)mDesc.countY; y++)
47 {
48 const UINT32 yArrayIdx = y * mDesc.countX;
49 const UINT32 yDataIdx = y * pixelData->getRowPitch() * pixelSize;
50
51 for(UINT32 x = 0; x < (UINT32)mDesc.countX; x++)
52 {
53 const UINT32 arrayIdx = x + yArrayIdx + zArrayIdx;
54 const UINT32 dataIdx = x * pixelSize + yDataIdx + zDataIdx;
55
56 const Vector3& source = arrayIdx < valuesToCopy ? values[arrayIdx] : Vector3::ZERO;
57 UINT8* dest = data + dataIdx;
58 PixelUtil::packColor(source.x, source.y, source.z, 1.0f, PF_RGBA16F, dest);
59 }
60 }
61 }
62
63 mTexture = Texture::_createPtr(pixelData);
64 }
65
66 SPtr<ct::CoreObject> VectorField::createCore() const
67 {
68 ct::VectorField* vectorField = new (bs_alloc<ct::VectorField>()) ct::VectorField(mDesc, mTexture->getCore());
69
70 SPtr<ct::VectorField> vectorFieldPtr = bs_shared_ptr<ct::VectorField>(vectorField);
71 vectorFieldPtr->_setThisPtr(vectorFieldPtr);
72
73 return vectorFieldPtr;
74 }
75
76 SPtr<ct::VectorField> VectorField::getCore() const
77 {
78 return std::static_pointer_cast<ct::VectorField>(mCoreSpecific);
79 }
80
81 /************************************************************************/
82 /* SERIALIZATION */
83 /************************************************************************/
84
85 RTTITypeBase* VectorField::getRTTIStatic()
86 {
87 return VectorFieldRTTI::instance();
88 }
89
90 RTTITypeBase* VectorField::getRTTI() const
91 {
92 return VectorField::getRTTIStatic();
93 }
94
95 /************************************************************************/
96 /* STATICS */
97 /************************************************************************/
98 HVectorField VectorField::create(const VECTOR_FIELD_DESC& desc, const Vector<Vector3>& values)
99 {
100 SPtr<VectorField> vectorFieldPtr = _createPtr(desc, values);
101
102 return static_resource_cast<VectorField>(gResources()._createResourceHandle(vectorFieldPtr));
103 }
104
105 SPtr<VectorField> VectorField::_createPtr(const VECTOR_FIELD_DESC& desc, const Vector<Vector3>& values)
106 {
107 auto* vectorField = new (bs_alloc<VectorField>()) VectorField(desc, values);
108
109 SPtr<VectorField> vectorFieldPtr = bs_shared_ptr<VectorField>(vectorField);
110 vectorFieldPtr->_setThisPtr(vectorFieldPtr);
111 vectorFieldPtr->initialize();
112
113 return vectorFieldPtr;
114 }
115
116 SPtr<VectorField> VectorField::_createEmpty()
117 {
118 auto* vectorField = new (bs_alloc<VectorField>()) VectorField();
119
120 SPtr<VectorField> vectorFieldPtr = bs_shared_ptr<VectorField>(vectorField);
121 vectorFieldPtr->_setThisPtr(vectorFieldPtr);
122
123 return vectorFieldPtr;
124 }
125
126 namespace ct
127 {
128 VectorField::VectorField(const VECTOR_FIELD_DESC& desc, const SPtr<Texture>& texture)
129 :TVectorField(desc)
130 {
131 mTexture = texture;
132 }
133 }
134
135 bool FGAImporter::isExtensionSupported(const String& ext) const
136 {
137 String lowerCaseExt = ext;
138 StringUtil::toLowerCase(lowerCaseExt);
139
140 return lowerCaseExt == u8"fga";
141 }
142
143 bool FGAImporter::isMagicNumberSupported(const UINT8* magicNumPtr, UINT32 numBytes) const
144 {
145 return true; // Plain-text so we don't even check for magic number
146 }
147
148 SPtr<Resource> FGAImporter::import(const Path& filePath, SPtr<const ImportOptions> importOptions)
149 {
150 String data;
151 {
152 Lock fileLock = FileScheduler::getLock(filePath);
153
154 SPtr<DataStream> stream = FileSystem::openFile(filePath);
155 data = stream->getAsString();
156 }
157
158 auto chars = bs_managed_stack_alloc<char>((UINT32)data.size() + 1);
159 memcpy(chars, data.data(), data.size());
160 chars[data.size()] = '\0';
161
162 const auto parseInt = [](char* input, INT32& output)
163 {
164 char* start = input;
165 while(*input != '\0')
166 {
167 if(*input == ',')
168 {
169 *input = '\0';
170 output = (INT32)atoi(start);
171
172 return input + 1;
173 }
174
175 input++;
176 }
177
178 return input;
179 };
180
181 const auto parseFloat = [](char* input, float& output)
182 {
183 char* start = input;
184 while(*input != '\0')
185 {
186 if(*input == ',')
187 {
188 *input = '\0';
189 output = (float)atof(start);
190
191 return input + 1;
192 }
193
194 input++;
195 }
196
197 return input;
198 };
199
200 VECTOR_FIELD_DESC desc;
201 char* readPos = chars;
202
203 // Read X, Y, Z sizes
204 Vector3I size;
205 readPos = parseInt(readPos, size.x);
206 readPos = parseInt(readPos, size.y);
207 readPos = parseInt(readPos, size.z);
208
209 if(size.x < 0 || size.y < 0 || size.z < 0)
210 {
211 LOGERR("Invalid dimensions.");
212 return nullptr;
213 }
214
215 desc.countX = (UINT32)size.x;
216 desc.countY = (UINT32)size.y;
217 desc.countZ = (UINT32)size.z;
218
219 if(*readPos == '\0')
220 {
221 LOGERR("Unexpected end of file.");
222 return nullptr;
223 }
224
225 Vector3 minBounds, maxBounds;
226 readPos = parseFloat(readPos, minBounds.x);
227 readPos = parseFloat(readPos, minBounds.y);
228 readPos = parseFloat(readPos, minBounds.z);
229 readPos = parseFloat(readPos, maxBounds.x);
230 readPos = parseFloat(readPos, maxBounds.y);
231 readPos = parseFloat(readPos, maxBounds.z);
232
233 if(*readPos == '\0')
234 {
235 LOGERR("Unexpected end of file.");
236 return nullptr;
237 }
238
239 desc.bounds = AABox(minBounds, maxBounds);
240
241 const UINT32 count = size.x * size.y * size.z;
242 Vector<Vector3> values;
243 values.resize(count);
244
245 for(UINT32 i = 0; i < count; i++)
246 {
247 readPos = parseFloat(readPos, values[i].x);
248 readPos = parseFloat(readPos, values[i].y);
249 readPos = parseFloat(readPos, values[i].z);
250
251 if ((i != (count - 1)) && *readPos == '\0')
252 {
253 LOGERR("Unexpected end of file.");
254 return nullptr;
255 }
256 }
257
258 if(*readPos != '\0')
259 {
260 LOGWRN("Unexpected excess data. This might indicate corrupt data. Remaining data will be truncated.");
261 }
262
263 const String fileName = filePath.getFilename(false);
264 SPtr<VectorField> vectorField = VectorField::_createPtr(desc, values);
265 vectorField->setName(fileName);
266
267 return vectorField;
268 }
269}
270