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