void ChatUser::set_session(SessionPtr session) { session_ = session; if (session_ != nullptr) { boost::system::error_code ec; offline_timer_.cancel(ec); if (ec) { CS_LOG_ERROR("offline_timer_ cancel failed: " << ec.message()); } std::weak_ptr<ChatUser> wself(shared_from_this()); session_->set_on_message_cb([wself](SessionPtr session, uint16_t type, const void* body, int16_t size) -> bool { if (ChatUserPtr self = wself.lock()) { return self->HandleMessage(session, type, body, size); } else { return false; } }); session_->set_on_close_cb([wself](SessionPtr session, CloseReason reason) { if (ChatUserPtr self = wself.lock()) { self->OnClose(session, reason); } }); } }
//---------------------------------------------------------------------------- //---------------------------------------------------------------------------- void CSAnimProvider::ReadSkinnedAnimationFromFile(StorageLocation in_location, const std::string& in_filePath, const ResourceProvider::AsyncLoadDelegate& in_delegate, const SkinnedAnimationSPtr& out_resource) const { IBinaryInputStreamUPtr stream = Application::Get()->GetFileSystem()->CreateBinaryInputStream(in_location, in_filePath); u32 numFrames = 0; s32 numSkeletonNodes = 0; if(ReadHeader(stream, in_filePath, out_resource, numFrames, numSkeletonNodes) == false) { CS_LOG_ERROR("Failed to read header in anim: " + in_filePath); out_resource->SetLoadState(Resource::LoadState::k_failed); if(in_delegate != nullptr) { Application::Get()->GetTaskScheduler()->ScheduleTask(TaskType::k_mainThread, [=](const TaskContext&) noexcept { in_delegate(out_resource); }); } return; } ReadAnimationData(stream, numFrames, numSkeletonNodes, out_resource); out_resource->SetLoadState(Resource::LoadState::k_loaded); if(in_delegate != nullptr) { Application::Get()->GetTaskScheduler()->ScheduleTask(TaskType::k_mainThread, [=](const TaskContext&) noexcept { in_delegate(out_resource); }); } }
//---------------------------------------------------------- /// Apply Inverse Bind Pose //---------------------------------------------------------- void SkinnedAnimationGroup::ApplyInverseBindPose(const std::vector<Core::Matrix4>& inInverseBindPoseMatrices, std::vector<Core::Matrix4>& outCombinedMatrices) { const std::vector<s32>& kadwJoints = mpSkeleton->GetJointIndices(); outCombinedMatrices.clear(); for (u32 i = 0; i < kadwJoints.size(); ++i) { outCombinedMatrices.push_back(Core::Matrix4()); } //check that they have the same number of joints if (kadwJoints.size() != inInverseBindPoseMatrices.size()) { CS_LOG_ERROR("Cannot apply bind pose matrices to joint matrices, because they are not from the same skeleton."); } //iterate through and multiply together to get the new array s32 count = 0; std::vector<s32>::const_iterator joint = kadwJoints.begin(); for (std::vector<Core::Matrix4>::const_iterator ibp = inInverseBindPoseMatrices.begin(); joint != kadwJoints.end() && ibp != inInverseBindPoseMatrices.end();) { //multiply together outCombinedMatrices[count] = ((*ibp) * (mCurrentAnimationMatrices[*joint])); //incriment the iterators ++joint; ++ibp; count++; } }
//-------------------------------------------------------- /// Create View /// /// Recursive function used to create subviews /// /// @param View XML element /// @return Created view //-------------------------------------------------------- GUIViewUPtr GUIViewFactory::CreateView(Core::XML::Node* inpViewElement) { //Get the view type //Get the param dictionary config values GUIViewUPtr pView; std::string strType; std::string strSource; bool bExtern = false; Core::StorageLocation eStorageLoc = Core::StorageLocation::k_none; for(Core::XML::Attribute* pAttr = Core::XMLUtils::GetFirstAttribute(inpViewElement); pAttr != nullptr; pAttr = Core::XMLUtils::GetNextAttribute(pAttr)) { if(Core::XMLUtils::GetName(pAttr) == "Type") { strType = Core::XMLUtils::GetValue(pAttr); } else if(Core::XMLUtils::GetName(pAttr) == "Source") { bExtern = true; strSource = Core::XMLUtils::GetValue(pAttr); } else if(Core::XMLUtils::GetName(pAttr) == "StorageLocation") { bExtern = true; eStorageLoc = Core::ParseStorageLocation(Core::XMLUtils::GetValue(pAttr)); } } //Create the UI of the given type MapDelegateToString::iterator it = mmapDelegateToType.find(strType); if(it != mmapDelegateToType.end()) { Core::ParamDictionary sParams = Core::ParamDictionarySerialiser::FromString(inpViewElement->value()); //Lets load the given type! pView = (it->second)(sParams); if(bExtern) { pView->AddSubview(CreateGUIViewFromScript(eStorageLoc, strSource)); } //Now we need to do some recursion and load any subviews for(Core::XML::Node* pNode = Core::XMLUtils::GetFirstChildElement(inpViewElement); pNode != nullptr; pNode = Core::XMLUtils::GetNextSiblingElement(pNode)) { pView->AddSubview(CreateView(pNode)); } return pView; } else { CS_LOG_ERROR("Cannot create GUI view of type: " + strType); return pView; } }
bool ChatUser::HandleRoomChatRequest(const RoomChatRequest& message) { ChatRoomPtr room = server_->FindRoom(message.room_id()); if (room != nullptr) { room->HandleRoomChatRequest(shared_from_this(), message); } else { CS_LOG_ERROR("ChatUser::HandleRoomChatRequest: room not found"); } return true; }
//---------------------------------------------------------- /// Blend Group //---------------------------------------------------------- void SkinnedAnimationGroup::BlendGroup(AnimationBlendType ineBlendType, const SkinnedAnimationGroupSPtr& inpAnimationGroup, f32 infBlendFactor) { switch (ineBlendType) { case AnimationBlendType::k_linear: mCurrentAnimationData = LerpBetweenFrames(mCurrentAnimationData.get(), inpAnimationGroup->mCurrentAnimationData.get(), infBlendFactor); break; default: CS_LOG_ERROR("Invalid animation blend type given."); break; } }
//---------------------------------------------------------------------------- //---------------------------------------------------------------------------- bool Mesh::Build(const MeshDescriptor& in_meshDesc) { bool bSuccess = true; //set the bounds SetBounds(in_meshDesc.mvMinBounds, in_meshDesc.mvMaxBounds); if (in_meshDesc.mFeatures.mbHasAnimationData == true) { m_skeleton = SkeletonUPtr(new Skeleton()); m_skeleton->Build(in_meshDesc.m_skeletonDesc); } //iterate through each mesh int count = 0; for (auto it = in_meshDesc.mMeshes.begin(); it != in_meshDesc.mMeshes.end(); ++it) { //caclulate the mesh capacities u32 udwVertexDataCapacity = it->mudwNumVertices * in_meshDesc.mVertexDeclaration.GetTotalSize(); u32 udwIndexDataCapacity = it->mudwNumIndices * in_meshDesc.mudwIndexSize; //prepare the mesh if it needs it, otherwise just update the vertex and index declarations. SubMesh* newSubMesh = CreateSubMesh(it->mstrName); newSubMesh->Prepare(Core::Application::Get()->GetRenderSystem(), in_meshDesc.mVertexDeclaration, in_meshDesc.mudwIndexSize, udwVertexDataCapacity, udwIndexDataCapacity, BufferAccess::k_read, it->ePrimitiveType); //check that the buffers are big enough to hold this data. if not throw an error. if (udwVertexDataCapacity <= newSubMesh->GetInternalMeshBuffer()->GetVertexCapacity() && udwIndexDataCapacity <= newSubMesh->GetInternalMeshBuffer()->GetIndexCapacity()) { newSubMesh->Build(it->mpVertexData, it->mpIndexData, it->mudwNumVertices, it->mudwNumIndices, it->mvMinBounds, it->mvMaxBounds); } else { CS_LOG_ERROR("Sub mesh data exceeds its buffer capacity. Mesh will return empty!"); bSuccess = false; } //add the skeleton controller if (in_meshDesc.mFeatures.mbHasAnimationData == true) { InverseBindPosePtr ibp(new InverseBindPose()); ibp->mInverseBindPoseMatrices = it->mInverseBindPoseMatrices; newSubMesh->SetInverseBindPose(ibp); } count++; } CalcVertexAndIndexCounts(); //return success return bSuccess; }
bool ChatUser::HandleUserLogoutRequest() { auto iter = entered_rooms_.begin(); while (iter != entered_rooms_.end()) { const room_id_t room_id = *iter++; ChatRoomPtr room = server_->FindRoom(room_id); if (room != nullptr) { room->HandleUserLogoutRequest(shared_from_this()); } else { CS_LOG_ERROR("ChatUser::HandleUserLogoutRequest: room not found"); } } return false; }
//------------------------------------------------------------ //------------------------------------------------------------ void GameState::OnDestroy() { m_scoreChangedConnection.reset(); // update all profiles PROFILE_UPDATE(); // create directory for profiling (won't create it if it already exists) bool isCreated = CSCore::Application::Get()->GetFileSystem()->CreateDirectoryPath(CSCore::StorageLocation::k_saveData, "Profile"); if (isCreated) { // get both parts of profile string std::string profileStr = PROFILE_GET_FLAT_STRING(); profileStr.append("\n\n"); profileStr.append(PROFILE_GET_TREE_STRING()); // write profile bool isWritten = CSCore::Application::Get()->GetFileSystem()->WriteFile(CSCore::StorageLocation::k_saveData, "Profile/cspong_profile.txt", profileStr); if (!isWritten) { CS_LOG_ERROR("PROFILING OUTPUT: Failed to write profile file to SaveData/Profile/cspong_profile.txt"); } else { CS_LOG_VERBOSE("PROFILING OUTPUT: Saved profile file to SaveData/Profile/cspong_profile.txt"); } } else { CS_LOG_ERROR("PROFILING OUTPUT: Failed to create directory path for SaveData/Profile"); } }
//--------------------------------------------------------- //--------------------------------------------------------- u8 HexToDec(const u8* inpHex) { if(*inpHex >= '0' && *inpHex <= '9') { return *inpHex - '0'; } else if(*inpHex >= 'A' && *inpHex <= 'F') { return 10 + (*inpHex - 'A'); } else { CS_LOG_ERROR("Invalid hex value of \""+ToString(*inpHex)+"\""); } return -1; }
//---------------------------------------------------------- /// Calculate Animation Frame //---------------------------------------------------------- SkinnedAnimation::FrameCUPtr SkinnedAnimationGroup::CalculateAnimationFrame(const SkinnedAnimationCSPtr& inpAnimation, f32 infPlaybackPosition) { //report errors if the playback position provided does not make sense if (infPlaybackPosition < 0.0f) { CS_LOG_ERROR("A playback position below 0 does not make sense."); } //calculate the two frame indices this is between f32 frames = infPlaybackPosition / inpAnimation->GetFrameTime(); s32 dwFrameAIndex = (s32)floorf(frames); s32 dwFrameBIndex = (s32)ceilf(frames); //ensure the frame numbers make sense if(dwFrameAIndex < 0) { dwFrameAIndex = 0; } if(dwFrameAIndex >= (s32)inpAnimation->GetNumFrames()) { dwFrameAIndex = inpAnimation->GetNumFrames() - 1; } if(dwFrameBIndex < 0) { dwFrameBIndex = 0; } if (dwFrameBIndex >= (s32)inpAnimation->GetNumFrames()) { dwFrameBIndex = inpAnimation->GetNumFrames() - 1; } //get the frames const SkinnedAnimation::Frame* frameA = inpAnimation->GetFrameAtIndex(dwFrameAIndex); const SkinnedAnimation::Frame* frameB = inpAnimation->GetFrameAtIndex(dwFrameBIndex); //get the ratio of one frame to the next f32 interpFactor = (infPlaybackPosition - (dwFrameAIndex * inpAnimation->GetFrameTime())) / inpAnimation->GetFrameTime(); //blend between frames return LerpBetweenFrames(frameA, frameB, interpFactor); }
//------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------ ResourceProvider* ResourcePool::FindProvider(const std::string& in_filePath, const PoolDesc& in_desc) { //Find the resource provider that can handle this extension std::string fileName; std::string fileExtension; StringUtils::SplitBaseFilename(in_filePath, fileName, fileExtension); u32 numProviders = static_cast<u32>(in_desc.m_providers.size()); for(u32 i=0; i<numProviders; ++i) { if(in_desc.m_providers[i]->CanCreateResourceWithFileExtension(fileExtension)) { return in_desc.m_providers[i]; } } CS_LOG_ERROR("Failed to find resource provider for extension " + fileExtension); return nullptr; }
//------------------------------------------------------------------------------------ /// At this stage in the app lifecycle all app and system references to resource /// should have been released. If the resource pool still has resources then this /// indicated leaks. //------------------------------------------------------------------------------------ void ResourcePool::Destroy() { ReleaseAllUnused(); bool error = false; for(auto& descEntry : m_descriptors) { for(auto itResource = descEntry.second.m_cachedResources.begin(); itResource != descEntry.second.m_cachedResources.end(); ++itResource) { //The pool is the sole owner so we can safely release the object CS_LOG_ERROR("Resource still in use: " + itResource->second->GetName()); error = true; } } if(error == true) { CS_LOG_FATAL("Resources are still in use. Indicates that there is leaky references"); } }
//---------------------------------------------------------- /// Calculate Animation Length //---------------------------------------------------------- void SkinnedAnimationGroup::CalculateAnimationLength() { if (mbAnimationLengthDirty) { mfAnimationLength = 0.0f; for (std::vector<AnimationItemPtr>::iterator it = mAnimations.begin(); it != mAnimations.end(); ++it) { const SkinnedAnimationCSPtr& pAnim = (*it)->pSkinnedAnimation; f32 fAnimationLength = pAnim->GetFrameTime() * ((f32)(pAnim->GetNumFrames() - 1)); if (mfAnimationLength != 0.0f && mfAnimationLength != fAnimationLength) { CS_LOG_ERROR("All grouped animations must have the same length!"); mfAnimationLength = 0.0f; return; } mfAnimationLength = fAnimationLength; } mbAnimationLengthDirty = false; } }
//-------------------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------------------- HttpRequest* HttpRequestSystem::MakeRequest(HttpRequest::Type in_type, const std::string& in_url, const std::string& in_body, const CSCore::ParamDictionary& in_headers, const HttpRequest::Delegate& in_delegate, u32 in_timeoutSecs) { CS_ASSERT(CSCore::Application::Get()->GetTaskScheduler()->IsMainThread() == true, "Http requests can currently only be made on the main thread"); CS_ASSERT(in_delegate != nullptr, "Cannot make an http request with a null delegate"); CS_ASSERT(in_url.empty() == false, "Cannot make an http request to a blank url"); URL_COMPONENTS urlComps; //Initialize the URL_COMPONENTS structure. ZeroMemory(&urlComps, sizeof(URL_COMPONENTS)); urlComps.dwStructSize = sizeof(URL_COMPONENTS); //Set required component lengths to non-zero so that they are cracked. urlComps.dwSchemeLength = (DWORD)-1; urlComps.dwHostNameLength = (DWORD)-1; urlComps.dwUrlPathLength = (DWORD)-1; urlComps.dwExtraInfoLength = (DWORD)-1; //Change the URL to wide string std::wstring url(WindowsStringUtils::UTF8ToUTF16(in_url)); //Crack the URL. if (!WinHttpCrackUrl(url.c_str(), (DWORD)url.length(), 0, &urlComps)) { CS_LOG_FATAL("Cannot crack URL: " + in_url); return nullptr; } //Weirdly the cracked URL struct only gives you the ability to crack your own URL std::wstring hostName = urlComps.lpszHostName; hostName = hostName.substr(0, urlComps.dwHostNameLength); HINTERNET connectionHandle = ::WinHttpConnect(m_sessionHandle, hostName.c_str(), INTERNET_DEFAULT_PORT, 0); if (!connectionHandle) { CS_LOG_ERROR("Failed to connect to server: " + in_url); return nullptr; } //Set up the request based on whether it is POST or GET and whether it is SSL LPCWSTR type = (in_type == CSNetworking::HttpRequest::Type::k_get ? L"GET" : L"POST"); HINTERNET requestHandle = 0; std::wstring urlPath = urlComps.lpszUrlPath; urlPath = urlPath.substr(0, urlComps.dwUrlPathLength); if (urlComps.nScheme == INTERNET_SCHEME_HTTPS) { requestHandle = ::WinHttpOpenRequest(connectionHandle, type, urlPath.c_str(), L"HTTP/1.1", WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, WINHTTP_FLAG_SECURE); if (requestHandle == nullptr || ApplySSLSettings(requestHandle) == false) { CS_LOG_ERROR("Failed to open request: " + in_url); WinHttpCloseHandle(connectionHandle); return nullptr; } } else { requestHandle = ::WinHttpOpenRequest(connectionHandle, type, urlPath.c_str(), L"HTTP/1.1", WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, 0); if (requestHandle == nullptr) { CS_LOG_ERROR("Failed to open request: " + in_url); WinHttpCloseHandle(connectionHandle); return nullptr; } } if (in_headers.empty() == false) { std::wstring headerBlob = ConvertHeaders(in_headers); if (WinHttpAddRequestHeaders(requestHandle, headerBlob.c_str(), DWORD(headerBlob.length()), WINHTTP_ADDREQ_FLAG_ADD) == false) { CS_LOG_ERROR("Failed to add http headers: " + in_url); WinHttpCloseHandle(requestHandle); WinHttpCloseHandle(connectionHandle); return nullptr; } } HttpRequest* httpRequest = new HttpRequest(in_type, in_url, in_body, in_headers, in_timeoutSecs, requestHandle, connectionHandle, GetMaxBufferSize(), in_delegate); m_requests.push_back(httpRequest); return httpRequest; }
//---------------------------------------------------------- /// Build Animation Data //---------------------------------------------------------- void SkinnedAnimationGroup::BuildAnimationData(AnimationBlendType ineBlendType, f32 infPlaybackPosition, f32 infBlendlinePosition) { //check how many animations we have. if we only have 1 then dont try and blend. If we have none then error. if (mAnimations.size() > 1) { //find which two animations should be blended together AnimationItemPtr pAnimItem1; AnimationItemPtr pAnimItem2; for (std::vector<AnimationItemPtr>::iterator it = mAnimations.begin(); it != mAnimations.end(); ++it) { f32 fBlendlinePosition = (*it)->fBlendlinePosition; if (fBlendlinePosition <= infBlendlinePosition && (pAnimItem1 == nullptr || fBlendlinePosition > pAnimItem1->fBlendlinePosition)) { pAnimItem1 = (*it); } if (fBlendlinePosition >= infBlendlinePosition && (pAnimItem2 == nullptr || fBlendlinePosition < pAnimItem2->fBlendlinePosition)) { pAnimItem2 = (*it); } } //get the animation frames SkinnedAnimation::FrameCUPtr pFrame1; if (pAnimItem1 != nullptr) pFrame1 = CalculateAnimationFrame(pAnimItem1->pSkinnedAnimation, infPlaybackPosition); SkinnedAnimation::FrameCUPtr pFrame2; if (pAnimItem2 != nullptr) pFrame2 = CalculateAnimationFrame(pAnimItem2->pSkinnedAnimation, infPlaybackPosition); //check that we do indeed have two animations to blend. if not, just return the frame we do have. if (pFrame1 != nullptr && pFrame2 != nullptr && pAnimItem1.get() != pAnimItem2.get()) { //get the interpolation factor and then apply the requested blend to the two frames. f32 fFactor = (infBlendlinePosition - pAnimItem1->fBlendlinePosition) / (pAnimItem2->fBlendlinePosition - pAnimItem1->fBlendlinePosition); switch (ineBlendType) { case AnimationBlendType::k_linear: mCurrentAnimationData = LerpBetweenFrames(pFrame1.get(), pFrame2.get(), fFactor); break; default: CS_LOG_ERROR("Invalid animation blend type given."); mCurrentAnimationData = std::move(pFrame1); break; } } else if (pFrame1 != nullptr) { mCurrentAnimationData = std::move(pFrame1); } else if (pFrame2 != nullptr) { mCurrentAnimationData = std::move(pFrame2); } else { CS_LOG_ERROR("Something has gone wrong when blending animations."); return; } mbPrepared = true; } else if (mAnimations.size() > 0) { const SkinnedAnimationCSPtr& pAnim = mAnimations[0]->pSkinnedAnimation; mCurrentAnimationData = CalculateAnimationFrame(pAnim, infPlaybackPosition); mbPrepared = true; } else { CS_LOG_ERROR("No animations attached to the animation group!"); return; } }
bool ChatUser::HandleMessage(SessionPtr session, uint16_t message_type, const void *body, int16_t size) { switch (message_type) { case MSG_USER_LOGOUT: { UserLogoutRequest message; if (!message.ParseFromArray(body, size)) { break; } return HandleUserLogoutRequest(); } case MSG_CREAT_ROOM: { CreatRoomRequest message; if (!message.ParseFromArray(body, size)) { break; } return HandleCreatRoomRequest(message); } case MSG_ENTER_ROOM: { EnterRoomRequest message; if (!message.ParseFromArray(body, size)) { break; } return HandleEnterRoomRequest(message); } case MSG_ROOM_MSG: { RoomChatRequest message; if (!message.ParseFromArray(body, size)) { break; } return HandleRoomChatRequest(message); } case MSG_LEAVE_ROOM: { LeaveRoomRequest message; if (!message.ParseFromArray(body, size)) { break; } return HandleLeaveRoomRequest(message); } break; case MSG_PING: { Ping message; if (!message.ParseFromArray(body, size)) { break; } return HandlePing(message); } break; case MSG_KEEP_ALIVE: { KeepAliveRequest message; if (!message.ParseFromArray(body, size)) { break; } return HandleKeepAliveRequest(message); } break; default: CS_LOG_ERROR("unknown message type"); break; } CS_LOG_ERROR("message parse failed"); return false; }
//------------------------------------------- //------------------------------------------- void Font::Build(const Descriptor& in_desc) { CS_ASSERT(in_desc.m_supportedCharacters.size() > 0, "Font: Cannot build characters with empty character set"); m_characterInfos.clear(); m_characters = in_desc.m_supportedCharacters; m_texture = in_desc.m_texture; const f32 textureAtlasWidth = (f32)in_desc.m_textureAtlasWidth; const f32 textureAtlasHeight = (f32)in_desc.m_textureAtlasHeight; u32 frameIdx = 0; auto it = m_characters.begin(); while(it < m_characters.end()) { auto character = Core::UTF8StringUtils::Next(it); CharacterInfo info; const Frame& frame = in_desc.m_frames[frameIdx]; info.m_UVs.m_u = (f32)(frame.m_texCoordU - 0.5f) / textureAtlasWidth; info.m_UVs.m_v = (f32)(frame.m_texCoordV - 0.5f) / textureAtlasHeight; info.m_UVs.m_s = (f32)(frame.m_width + 1.0f) / textureAtlasWidth; info.m_UVs.m_t = (f32)(frame.m_height + 1.0f) / textureAtlasHeight; info.m_size.x = frame.m_width; info.m_size.y = frame.m_height; info.m_offset.x = frame.m_offsetX; info.m_offset.y = frame.m_offsetY; m_lineHeight = std::max((f32)frame.m_height, m_lineHeight); m_characterInfos.insert(std::make_pair(character, info)); ++frameIdx; } if(in_desc.m_lineHeight > 0) { m_lineHeight = (f32)in_desc.m_lineHeight; } //Just assign the width of a whitespaces based on the similar space character in the //font. This means it will scale relative to the font CharacterInfo info; if(TryGetCharacterInfo(k_similarSpaceCharacter, info) == false) { CS_LOG_ERROR("Cannot find similar space character in font: " + GetName()); info.m_size.x = 1.0f; info.m_offset = Core::Vector2::k_zero; info.m_UVs = Rendering::UVs(); } //Space info.m_size.y = 0.0f; m_characterInfos.insert(std::make_pair(k_spaceCharacter, info)); //Non-breaking space m_characterInfos.insert(std::make_pair(k_nbspCharacter, info)); //Tab info.m_size.x *= k_spacesPerTab; m_characterInfos.insert(std::make_pair(k_tabCharacter, info)); //Return m_characterInfos.insert(std::make_pair(k_returnCharacter, CharacterInfo())); }
//---------------------------------------------------------------------------- //---------------------------------------------------------------------------- void CubemapProvider::LoadCubemap(Core::StorageLocation in_location, const std::string& in_filePath, const Core::IResourceOptionsBaseCSPtr& in_options, const Core::ResourceProvider::AsyncLoadDelegate& in_delegate, const Core::ResourceSPtr& out_resource) { //read the Cubemap JSON Json::Value jsonRoot; if (Core::JsonUtils::ReadJson(in_location, in_filePath, jsonRoot) == false) { CS_LOG_ERROR("Could not read face Cubemap file '" + in_filePath + "'."); out_resource->SetLoadState(Core::Resource::LoadState::k_failed); if(in_delegate != nullptr) { Core::Application::Get()->GetTaskScheduler()->ScheduleMainThreadTask(std::bind(in_delegate, out_resource)); } return; } const u32 k_numFaces = 6; const std::string k_faces[k_numFaces] = {"Right", "Left", "Top", "Bottom", "Front", "Back"}; //MSVC does not support moving arrays of unique_ptr at this time and therefore we have //to create a shared pointer in order to pass it into the lambda auto imageDataContainer = std::make_shared<std::array<Texture::TextureDataUPtr, k_numFaces>>(); std::array<Texture::Descriptor, k_numFaces> descs; for(u32 i = 0; i < k_numFaces; ++i) { auto textureFile = ParseCubemapFace(jsonRoot, k_faces[i]); if (textureFile.first == Core::StorageLocation::k_none || textureFile.second == "") { CS_LOG_ERROR("Could not load face '" + k_faces[i] + "' in Cubemap '" + in_filePath + "'."); out_resource->SetLoadState(Core::Resource::LoadState::k_failed); if(in_delegate != nullptr) { Core::Application::Get()->GetTaskScheduler()->ScheduleMainThreadTask(std::bind(in_delegate, out_resource)); } return; } Core::ImageSPtr image(Core::Image::Create()); if (LoadImage(image, m_imageProviders, textureFile.first, textureFile.second) == false) { CS_LOG_ERROR("Could not load image '" + textureFile.second + "' in Cubemap '" + in_filePath + "'"); out_resource->SetLoadState(Core::Resource::LoadState::k_failed); if(in_delegate != nullptr) { Core::Application::Get()->GetTaskScheduler()->ScheduleMainThreadTask(std::bind(in_delegate, out_resource)); } return; } (*imageDataContainer)[i] = image->MoveData(); Texture::Descriptor desc; desc.m_width = image->GetWidth(); desc.m_height = image->GetHeight(); desc.m_format = image->GetFormat(); desc.m_compression = image->GetCompression(); desc.m_dataSize = image->GetDataSize(); descs[i] = std::move(desc); } if(in_delegate == nullptr) { Cubemap* cubemap = (Cubemap*)out_resource.get(); const CubemapResourceOptions* options = (const CubemapResourceOptions*)in_options.get(); cubemap->Build(descs, std::move(*imageDataContainer), options->IsMipMapsEnabled(), options->IsRestoreCubemapDataEnabled()); cubemap->SetWrapMode(options->GetWrapModeS(), options->GetWrapModeT()); cubemap->SetFilterMode(options->GetFilterMode()); out_resource->SetLoadState(Core::Resource::LoadState::k_loaded); } else { auto task([descs, imageDataContainer, in_options, in_delegate, out_resource]() { Cubemap* cubemap = (Cubemap*)out_resource.get(); const CubemapResourceOptions* options = (const CubemapResourceOptions*)in_options.get(); cubemap->Build(descs, std::move(*imageDataContainer), options->IsMipMapsEnabled(), options->IsRestoreCubemapDataEnabled()); cubemap->SetWrapMode(options->GetWrapModeS(), options->GetWrapModeT()); cubemap->SetFilterMode(options->GetFilterMode()); out_resource->SetLoadState(Core::Resource::LoadState::k_loaded); in_delegate(out_resource); }); Core::Application::Get()->GetTaskScheduler()->ScheduleMainThreadTask(task); } }