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 | |
10 | namespace 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 | |