diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt deleted file mode 100644 index 4ed756df..00000000 --- a/Source/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -set(SHADER_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Shaders) - -add_subdirectory(ShaderCookerStandalone) -add_subdirectory(Shaders) -add_subdirectory(Game) \ No newline at end of file diff --git a/Source/Game/Game.lua b/Source/Game/Game.lua index 36347ec9..53be5030 100644 --- a/Source/Game/Game.lua +++ b/Source/Game/Game.lua @@ -52,6 +52,6 @@ Solution.Util.CreateConsoleApp(gameAppMod.Name, Solution.Projects.Current.BinDir "**.ico" } Solution.Util.SetFiles(appIconFiles) - vpaths { ['Resources/*'] = { '*.rc', '**.ico' } } + vpaths { ['Resources/**'] = { '*.rc', '**.ico' } } end) end) \ No newline at end of file diff --git a/Source/Game/Game/Animation/AnimationSystem.cpp b/Source/Game/Game/Animation/AnimationSystem.cpp index 1f4422e5..a0d288c8 100644 --- a/Source/Game/Game/Animation/AnimationSystem.cpp +++ b/Source/Game/Game/Animation/AnimationSystem.cpp @@ -8,6 +8,8 @@ #include +#include + AutoCVar_Int CVAR_AnimationSystemEnabled(CVarCategory::Client | CVarCategory::Rendering, "animationEnabled", "Enables the Animation System", 0, CVarFlags::EditCheckbox); AutoCVar_Float CVAR_AnimationSystemTimeScale(CVarCategory::Client | CVarCategory::Rendering, "animationTimeScale", "Controls the global speed of all animations", 1.0f); AutoCVar_Int CVAR_AnimationSystemThrottle(CVarCategory::Client | CVarCategory::Rendering, "animationThrottle", "Sets the number of dirty instances that can be updated every frame", 1024); @@ -69,25 +71,24 @@ namespace Animation return track.values[0]; } - u32 progressInMS = static_cast(progress * 1000.0f); - - u32 lastTimestamp = track.timestamps[numTimeStamps - 1]; - if (progressInMS >= lastTimestamp) + f32 lastTimestamp = static_cast(track.timestamps[numTimeStamps - 1] / 1000.0f); + if (progress >= lastTimestamp) return track.values[numTimeStamps - 1]; - u32 timestamp1 = track.timestamps[0]; - u32 timestamp2 = track.timestamps[1]; + f32 timestamp1 = static_cast(track.timestamps[0] / 1000.0f); + f32 timestamp2 = static_cast(track.timestamps[1] / 1000.0f); + T value1 = track.values[0]; T value2 = track.values[1]; for (u32 i = 1; i < numTimeStamps; i++) { - u32 timestamp = track.timestamps[i]; + f32 timestamp = static_cast(track.timestamps[i] / 1000.0f); - if (progressInMS <= timestamp) + if (progress <= timestamp) { - timestamp1 = track.timestamps[i - 1]; - timestamp2 = track.timestamps[i]; + timestamp1 = static_cast(track.timestamps[i - 1] / 1000.0f); + timestamp2 = static_cast(track.timestamps[i] / 1000.0f); value1 = track.values[i - 1]; value2 = track.values[i]; @@ -95,8 +96,12 @@ namespace Animation } } - f32 currentProgress = static_cast((progressInMS - timestamp1)); - f32 currentFrameDuration = static_cast((timestamp2 - timestamp1)); + f32 currentProgress = progress - timestamp1; + f32 currentFrameDuration = timestamp2 - timestamp1; + + if (currentFrameDuration == 0.0f) + return value1; + f32 t = currentProgress / currentFrameDuration; if constexpr (std::is_same_v) @@ -204,6 +209,11 @@ namespace Animation BoneNames[192] = "face_beard_00_M_JNT"; } + bool AnimationSystem::IsEnabled() + { + return CVAR_AnimationSystemEnabled.Get() != 0; + } + bool AnimationSystem::AddSkeleton(ModelID modelID, Model::ComplexModel& model) { bool isEnabled = CVAR_AnimationSystemEnabled.Get(); @@ -282,15 +292,9 @@ namespace Animation AnimationSkeletonBone& animBone = skeleton.bones[i]; animBone.info = bone; - - if (model.keyBoneIDToBoneIndex.contains(bone.keyBoneID)) - { - u32 keyBoneValue = model.keyBoneIDToBoneIndex[bone.keyBoneID]; - skeleton.keyBoneIDToBoneID[keyBoneValue] = i; - } } - skeleton.keyBoneIDToBoneID = model.keyBoneIDToBoneIndex; + skeleton.keyBoneIDToBoneIndex = model.keyBoneIDToBoneIndex; } if (numTextureTransforms) @@ -361,7 +365,14 @@ namespace Animation if (!instanceAlreadyExists) { u32 instanceIndex = _storage.instancesIndex.fetch_add(1); - _storage.instanceIDs[instanceIndex] = instanceID; + if (_storage.instanceIDs.size() <= instanceIndex) + { + _storage.instanceIDs.push_back(instanceID); + } + else + { + _storage.instanceIDs[instanceIndex] = instanceID; + } } if (HasModelRenderer()) @@ -372,7 +383,23 @@ namespace Animation return true; } - bool AnimationSystem::GetCurrentAnimation(InstanceID instanceID, Bone bone, Type* primary, Type* sequence) + bool AnimationSystem::RemoveInstance(InstanceID instanceID) + { + if (!HasInstance(instanceID)) + { + return false; + } + + std::erase(_storage.instanceIDs, instanceID); + _storage.instancesIndex--; + + _storage.instanceIDToData.erase(instanceID); + _storage.dirtyInstances.erase(instanceID); + + return true; + } + + bool AnimationSystem::GetCurrentAnimation(InstanceID instanceID, Bone bone, AnimationSequenceState* primary, AnimationSequenceState* sequence) { if (!HasInstance(instanceID)) { @@ -391,11 +418,17 @@ namespace Animation { if (const AnimationSequenceState* sequenceState = GetSequenceStateForBone(boneInstance)) { - *primary = sequenceState->animation; + *primary = *sequenceState; } else { - *primary = Type::Invalid; + primary->animation = Type::Invalid; + primary->sequenceID = InvalidSequenceID; + primary->finishedCallback = nullptr; + primary->flags = Flag::None; + primary->blendTimeStart = 0.0f; + primary->blendTimeEnd = 0.0f; + primary->progress = 0.0f; } } if (sequence) @@ -404,11 +437,17 @@ namespace Animation f32 transitionDuration = 0.0f; if (const AnimationSequenceState* sequenceState = GetDeferredSequenceStateForBone(boneInstance, timeToTransition, transitionDuration)) { - *sequence = sequenceState->animation; + *sequence = *sequenceState; } else { - *sequence = Type::Invalid; + sequence->animation = Type::Invalid; + sequence->sequenceID = InvalidSequenceID; + sequence->finishedCallback = nullptr; + sequence->flags = Flag::None; + sequence->blendTimeStart = 0.0f; + sequence->blendTimeEnd = 0.0f; + sequence->progress = 0.0f; } } @@ -422,15 +461,15 @@ namespace Animation return false; } - Type currentAnimation = Type::Invalid; - Type nextAnimation = Type::Invalid; + AnimationSequenceState currentAnimation = { }; + AnimationSequenceState nextAnimation = { }; if (!GetCurrentAnimation(instanceID, bone, ¤tAnimation, &nextAnimation)) { return false; } - return currentAnimation == animationID || nextAnimation == animationID; + return currentAnimation.animation == animationID || nextAnimation.animation == animationID; } u32 AnimationSystem::GetSequenceIDForAnimationID(ModelID modelID, Type animationID) @@ -460,13 +499,19 @@ namespace Animation return -1; if (bone == Bone::Default) + { bone = Bone::Main; + if (!skeleton.keyBoneIDToBoneIndex.contains((i16)bone)) + { + bone = Bone::Root; + } + } i16 keyBone = (i16)bone; - if (!skeleton.keyBoneIDToBoneID.contains(keyBone)) + if (!skeleton.keyBoneIDToBoneIndex.contains(keyBone)) return -1; - i16 boneID = skeleton.keyBoneIDToBoneID.at(keyBone); + i16 boneID = skeleton.keyBoneIDToBoneIndex.at(keyBone); if (boneID >= static_cast(numBones)) return -1; @@ -572,6 +617,8 @@ namespace Animation bool shouldBlend = false; + u16 blendTimeStartInMS = sequenceInfo.blendTimeStart; + if (blendOverride == BlendOverride::Auto) { shouldBlend = sequenceInfo.flags.blendTransition; @@ -586,10 +633,16 @@ namespace Animation { shouldBlend = true; } + + if (shouldBlend && blendTimeStartInMS == 0) + { + blendTimeStartInMS = 150; + } - f32 blendTimeStart = static_cast(sequenceInfo.blendTimeStart) / 1000.0f; + f32 blendTimeStart = static_cast(blendTimeStartInMS) / 1000.0f; boneInstance.timeToTransition = blendTimeStart * shouldBlend; boneInstance.transitionDuration = boneInstance.timeToTransition; + return true; } bool AnimationSystem::SetBoneRotation(InstanceID instanceID, Bone bone, quat& rotation) @@ -651,6 +704,10 @@ namespace Animation for (u32 globalLoopIndex = 0; globalLoopIndex < numGlobalLoops; globalLoopIndex++) { AnimationGlobalLoopInstance& globalLoop = instance.globalLoops[globalLoopIndex]; + + if (globalLoop.duration == 0.0f) + continue; + globalLoop.currentTime = fmod((globalLoop.currentTime + adjustedDeltaTime), globalLoop.duration); if (globalLoop.currentTime >= globalLoop.duration) @@ -667,11 +724,14 @@ namespace Animation HandleBoneAnimation(skeleton, instance, bone, boneInstance, adjustedDeltaTime); - if (!bone.info.flags.Transformed) - continue; - const mat4x4& originalMatrix = _storage.boneMatrices[instance.boneMatrixOffset + boneIndex]; - mat4x4 boneMatrix = GetBoneMatrix(skeleton, instance, bone, boneInstance); + + mat4x4 boneMatrix = mat4x4(1.0f); + + if (bone.info.flags.Transformed) + { + boneMatrix = GetBoneMatrix(skeleton, instance, bone, boneInstance); + } // Apply parent's transformation if (bone.info.parentBoneID != -1) @@ -734,12 +794,16 @@ namespace Animation u32 numInstancesToUpdate = glm::min(numDirty, throttle); u32 numUpdatedInstances = 0; - for (u32 i = 0; i < numInstancesToUpdate; i++) + while (numUpdatedInstances < numInstancesToUpdate && !_storage.dirtyInstancesQueue.empty()) { - if (numUpdatedInstances >= numInstancesToUpdate) - break; - InstanceID instanceID = _storage.dirtyInstancesQueue.front(); + + if (!_storage.dirtyInstances.contains(instanceID)) + { + _storage.dirtyInstancesQueue.pop(); + continue; + } + AnimationInstance& instance = _storage.instanceIDToData[instanceID]; const AnimationSkeleton& skeleton = _storage.skeletons[instance.modelID]; @@ -757,6 +821,8 @@ namespace Animation _storage.dirtyInstancesQueue.pop(); _storage.dirtyInstances.erase(instanceID); + + numUpdatedInstances++; } } } @@ -808,12 +874,6 @@ namespace Animation } void AnimationSystem::Clear() { - bool isEnabled = CVAR_AnimationSystemEnabled.Get(); - if (!isEnabled) - { - return; - } - _storage.skeletons.clear(); _storage.instancesIndex.store(0); @@ -831,7 +891,6 @@ namespace Animation mat4x4 AnimationSystem::GetBoneMatrix(const AnimationSkeleton& skeleton, const AnimationInstance& instance, const AnimationSkeletonBone& bone, const AnimationBoneInstance& boneInstance) { - mat4x4 boneMatrix = mat4x4(1.0f); vec3 translationValue = vec3(0.0f, 0.0f, 0.0f); quat rotationValue = quat(1.0f, 0.0f, 0.0f, 0.0f); vec3 scaleValue = vec3(1.0f, 1.0f, 1.0f); @@ -840,26 +899,35 @@ namespace Animation const AnimationBoneInstance* rotationBoneInstance = nullptr; const AnimationBoneInstance* scaleBoneInstance = nullptr; + bool isTranslationGlobalLoop = bone.info.translation.globalLoopIndex != -1; + bool isRotationGlobalLoop = bone.info.rotation.globalLoopIndex != -1; + bool isScaleGlobalLoop = bone.info.scale.globalLoopIndex != -1; + // Primary Sequence { const AnimationBoneInstance& rootBoneInstance = instance.bones[0]; // Handle Translation { - bool isGlobalLoop = bone.info.translation.globalLoopIndex != -1; translationBoneInstance = &boneInstance; - if (isGlobalLoop || translationBoneInstance->currentAnimation.animation == Type::Invalid && translationBoneInstance->nextAnimation.animation == Type::Invalid) + if (isTranslationGlobalLoop || translationBoneInstance->currentAnimation.animation == Type::Invalid && translationBoneInstance->nextAnimation.animation == Type::Invalid) { translationBoneInstance = &rootBoneInstance; } u32 sequenceID = translationBoneInstance->currentAnimation.sequenceID; + if (isTranslationGlobalLoop) + { + sequenceID = 0; + } + if (sequenceID < bone.info.translation.tracks.size()) { const Model::ComplexModel::AnimationTrack& track = bone.info.translation.tracks[sequenceID]; f32 progress = translationBoneInstance->currentAnimation.progress; + f32 maxDuration = 0.0f; if (bone.info.translation.globalLoopIndex != -1) { @@ -874,15 +942,19 @@ namespace Animation // Handle Rotation { - bool isGlobalLoop = bone.info.rotation.globalLoopIndex != -1; rotationBoneInstance = &boneInstance; - if (isGlobalLoop || rotationBoneInstance->currentAnimation.animation == Type::Invalid && rotationBoneInstance->nextAnimation.animation == Type::Invalid) + if (isRotationGlobalLoop || rotationBoneInstance->currentAnimation.animation == Type::Invalid && rotationBoneInstance->nextAnimation.animation == Type::Invalid) { rotationBoneInstance = &rootBoneInstance; } u32 sequenceID = rotationBoneInstance->currentAnimation.sequenceID; + if (isRotationGlobalLoop) + { + sequenceID = 0; + } + if (sequenceID < bone.info.rotation.tracks.size()) { const Model::ComplexModel::AnimationTrack& track = bone.info.rotation.tracks[sequenceID]; @@ -896,21 +968,27 @@ namespace Animation } if (track.values.size() > 0 && track.timestamps.size() > 0) + { rotationValue = InterpolateKeyframe(track, progress); + } } } // Handle Scale { - bool isGlobalLoop = bone.info.scale.globalLoopIndex != -1; scaleBoneInstance = &boneInstance; - if (isGlobalLoop || scaleBoneInstance->currentAnimation.animation == Type::Invalid && scaleBoneInstance->nextAnimation.animation == Type::Invalid) + if (isScaleGlobalLoop || scaleBoneInstance->currentAnimation.animation == Type::Invalid && scaleBoneInstance->nextAnimation.animation == Type::Invalid) { scaleBoneInstance = &rootBoneInstance; } u32 sequenceID = scaleBoneInstance->currentAnimation.sequenceID; + if (isScaleGlobalLoop) + { + sequenceID = 0; + } + if (sequenceID < bone.info.scale.tracks.size()) { const Model::ComplexModel::AnimationTrack& track = bone.info.scale.tracks[sequenceID]; @@ -931,95 +1009,86 @@ namespace Animation // Transition Sequence { - if (translationBoneInstance) + if (translationBoneInstance && !isTranslationGlobalLoop) { f32 timeToTransition = translationBoneInstance->timeToTransition; f32 transitionDuration = translationBoneInstance->transitionDuration; f32 transitionProgress = timeToTransition ? glm::clamp(1.0f - (timeToTransition / transitionDuration), 0.0f, 1.0f) : 1.0f; - u32 sequenceID = translationBoneInstance->nextAnimation.sequenceID; - if (sequenceID < bone.info.translation.tracks.size()) + if (transitionDuration > 0.0f) { - const Model::ComplexModel::AnimationTrack& track = bone.info.translation.tracks[sequenceID]; - - if (track.values.size() > 0 && track.timestamps.size() > 0) + u32 sequenceID = translationBoneInstance->nextAnimation.sequenceID; + if (sequenceID < bone.info.translation.tracks.size()) { - f32 progress = translationBoneInstance->nextAnimation.progress; + const Model::ComplexModel::AnimationTrack& track = bone.info.translation.tracks[sequenceID]; - if (bone.info.translation.globalLoopIndex != -1) + if (track.values.size() > 0 && track.timestamps.size() > 0) { - const AnimationGlobalLoopInstance& globalLoop = instance.globalLoops[bone.info.translation.globalLoopIndex]; - progress = globalLoop.currentTime; - } + f32 progress = translationBoneInstance->nextAnimation.progress; - vec3 translation = InterpolateKeyframe(track, progress); - translationValue = glm::mix(translationValue, translation, transitionProgress); + vec3 translation = InterpolateKeyframe(track, progress); + translationValue = glm::mix(translationValue, translation, transitionProgress); + } } } } - if (rotationBoneInstance) + if (rotationBoneInstance && !isRotationGlobalLoop) { f32 timeToTransition = rotationBoneInstance->timeToTransition; f32 transitionDuration = rotationBoneInstance->transitionDuration; f32 transitionProgress = timeToTransition ? glm::clamp(1.0f - (timeToTransition / transitionDuration), 0.0f, 1.0f) : 1.0f; - u32 sequenceID = rotationBoneInstance->nextAnimation.sequenceID; - if (sequenceID < bone.info.rotation.tracks.size()) + if (transitionDuration > 0.0f) { - const Model::ComplexModel::AnimationTrack& track = bone.info.rotation.tracks[sequenceID]; - - if (track.values.size() > 0 && track.timestamps.size() > 0) + u32 sequenceID = rotationBoneInstance->nextAnimation.sequenceID; + if (sequenceID < bone.info.rotation.tracks.size()) { - f32 progress = rotationBoneInstance->nextAnimation.progress; + const Model::ComplexModel::AnimationTrack& track = bone.info.rotation.tracks[sequenceID]; - if (bone.info.rotation.globalLoopIndex != -1) + if (track.values.size() > 0 && track.timestamps.size() > 0) { - const AnimationGlobalLoopInstance& globalLoop = instance.globalLoops[bone.info.rotation.globalLoopIndex]; - progress = globalLoop.currentTime; - } + f32 progress = rotationBoneInstance->nextAnimation.progress; - quat rotation = InterpolateKeyframe(track, progress); - rotationValue = glm::mix(rotationValue, rotation, transitionProgress); + quat rotation = InterpolateKeyframe(track, progress); + rotationValue = glm::slerp(rotationValue, rotation, transitionProgress); + } } } } - if (scaleBoneInstance) + if (scaleBoneInstance && !isScaleGlobalLoop) { f32 timeToTransition = scaleBoneInstance->timeToTransition; f32 transitionDuration = scaleBoneInstance->transitionDuration; f32 transitionProgress = timeToTransition ? glm::clamp(1.0f - (timeToTransition / transitionDuration), 0.0f, 1.0f) : 1.0f; - u32 sequenceID = scaleBoneInstance->nextAnimation.sequenceID; - if (sequenceID < bone.info.scale.tracks.size()) + if (transitionDuration > 0.0f) { - const Model::ComplexModel::AnimationTrack& track = bone.info.scale.tracks[sequenceID]; - - if (track.values.size() > 0 && track.timestamps.size() > 0) + u32 sequenceID = scaleBoneInstance->nextAnimation.sequenceID; + if (sequenceID < bone.info.scale.tracks.size()) { - f32 progress = scaleBoneInstance->nextAnimation.progress; + const Model::ComplexModel::AnimationTrack& track = bone.info.scale.tracks[sequenceID]; - if (bone.info.scale.globalLoopIndex != -1) + if (track.values.size() > 0 && track.timestamps.size() > 0) { - const AnimationGlobalLoopInstance& globalLoop = instance.globalLoops[bone.info.scale.globalLoopIndex]; - progress = globalLoop.currentTime; - } + f32 progress = scaleBoneInstance->nextAnimation.progress; - vec3 scale = InterpolateKeyframe(track, progress); - scaleValue = glm::mix(scaleValue, scale, transitionProgress); + vec3 scale = InterpolateKeyframe(track, progress); + scaleValue = glm::mix(scaleValue, scale, transitionProgress); + } } } } } - rotationValue = glm::normalize(boneInstance.rotationOffset * rotationValue); + rotationValue = glm::normalize(boneInstance.rotationOffset * glm::normalize(rotationValue)); const vec3& pivot = bone.info.pivot; - boneMatrix = mul(glm::translate(mat4x4(1.0f), pivot), boneMatrix); + mat4x4 boneMatrix = mul(glm::translate(mat4x4(1.0f), pivot), mat4x4(1.0f)); mat4x4 translationMatrix = glm::translate(mat4x4(1.0f), translationValue); - mat4x4 rotationMatrix = glm::toMat4(glm::normalize(rotationValue)); + mat4x4 rotationMatrix = glm::toMat4(rotationValue); mat4x4 scaleMatrix = glm::scale(mat4x4(1.0f), scaleValue); boneMatrix = mul(translationMatrix, boneMatrix); @@ -1030,6 +1099,7 @@ namespace Animation return boneMatrix; } + void AnimationSystem::HandleBoneAnimation(const AnimationSkeleton& skeleton, const AnimationInstance& instance, const AnimationSkeletonBone& bone, AnimationBoneInstance& boneInstance, f32 deltaTime) { auto HandleCurrentAnimation = [this](const AnimationSkeleton& skeleton, const AnimationInstance& instance, AnimationBoneInstance& boneInstance, f32 deltaTime) @@ -1041,8 +1111,9 @@ namespace Animation f32 sequenceDuration = static_cast(sequenceInfo.duration / 1000.0f); u32 progress = static_cast(boneInstance.currentAnimation.progress * 1000.0f); bool isInTransitionPhaseBefore = boneInstance.currentAnimation.progress >= sequenceDuration - boneInstance.transitionDuration; + bool canLoop = HasAnimationFlag(boneInstance.currentAnimation.flags, Flag::Loop); - if (progress >= sequenceInfo.duration) + if (progress >= sequenceInfo.duration && !canLoop) { if (!HasAnimationFlag(boneInstance.currentAnimation.flags, Flag::Frozen)) { @@ -1076,11 +1147,9 @@ namespace Animation } else { - f32 maxDiff = sequenceDuration - boneInstance.currentAnimation.progress; + boneInstance.currentAnimation.progress += deltaTime; - boneInstance.currentAnimation.progress += glm::clamp(deltaTime, 0.0f, maxDiff); - - if (HasAnimationFlag(boneInstance.currentAnimation.flags, Flag::Loop)) + if (canLoop) { bool isInTransitionPhaseAfter = boneInstance.currentAnimation.progress >= sequenceDuration - boneInstance.transitionDuration; if (!isInTransitionPhaseBefore && isInTransitionPhaseAfter) @@ -1175,28 +1244,36 @@ namespace Animation const AnimationBoneInstance* rotationBoneInstance = nullptr; const AnimationBoneInstance* scaleBoneInstance = nullptr; + bool isTranslationGlobalLoop = textureTransform.info.translation.globalLoopIndex != -1; + bool isRotationGlobalLoop = textureTransform.info.rotation.globalLoopIndex != -1; + bool isScaleGlobalLoop = textureTransform.info.scale.globalLoopIndex != -1; + // Primary Sequence { const AnimationBoneInstance& rootBoneInstance = instance.bones[0]; // Handle Translation { - bool isGlobalLoop = textureTransform.info.translation.globalLoopIndex != -1; translationBoneInstance = &boneInstance; - if (isGlobalLoop || translationBoneInstance->currentAnimation.animation == Type::Invalid && translationBoneInstance->nextAnimation.animation == Type::Invalid) + if (isTranslationGlobalLoop || translationBoneInstance->currentAnimation.animation == Type::Invalid && translationBoneInstance->nextAnimation.animation == Type::Invalid) { translationBoneInstance = &rootBoneInstance; } u32 sequenceID = translationBoneInstance->currentAnimation.sequenceID; + if (isTranslationGlobalLoop) + { + sequenceID = 0; + } + if (sequenceID < textureTransform.info.translation.tracks.size()) { const Model::ComplexModel::AnimationTrack& track = textureTransform.info.translation.tracks[sequenceID]; f32 progress = translationBoneInstance->currentAnimation.progress; - if (textureTransform.info.translation.globalLoopIndex != -1) + if (isTranslationGlobalLoop) { const AnimationGlobalLoopInstance& globalLoop = instance.globalLoops[textureTransform.info.translation.globalLoopIndex]; progress = globalLoop.currentTime; @@ -1209,22 +1286,26 @@ namespace Animation // Handle Rotation { - bool isGlobalLoop = textureTransform.info.rotation.globalLoopIndex != -1; rotationBoneInstance = &boneInstance; - if (isGlobalLoop || rotationBoneInstance->currentAnimation.animation == Type::Invalid && rotationBoneInstance->nextAnimation.animation == Type::Invalid) + if (isRotationGlobalLoop || rotationBoneInstance->currentAnimation.animation == Type::Invalid && rotationBoneInstance->nextAnimation.animation == Type::Invalid) { rotationBoneInstance = &rootBoneInstance; } u32 sequenceID = rotationBoneInstance->currentAnimation.sequenceID; + if (isRotationGlobalLoop) + { + sequenceID = 0; + } + if (sequenceID < textureTransform.info.rotation.tracks.size()) { const Model::ComplexModel::AnimationTrack& track = textureTransform.info.rotation.tracks[sequenceID]; f32 progress = rotationBoneInstance->currentAnimation.progress; - if (textureTransform.info.rotation.globalLoopIndex != -1) + if (isRotationGlobalLoop) { const AnimationGlobalLoopInstance& globalLoop = instance.globalLoops[textureTransform.info.rotation.globalLoopIndex]; progress = globalLoop.currentTime; @@ -1237,22 +1318,26 @@ namespace Animation // Handle Scale { - bool isGlobalLoop = textureTransform.info.scale.globalLoopIndex != -1; scaleBoneInstance = &boneInstance; - if (isGlobalLoop || scaleBoneInstance->currentAnimation.animation == Type::Invalid && scaleBoneInstance->nextAnimation.animation == Type::Invalid) + if (isScaleGlobalLoop || scaleBoneInstance->currentAnimation.animation == Type::Invalid && scaleBoneInstance->nextAnimation.animation == Type::Invalid) { scaleBoneInstance = &rootBoneInstance; } u32 sequenceID = scaleBoneInstance->currentAnimation.sequenceID; + if (isScaleGlobalLoop) + { + sequenceID = 0; + } + if (sequenceID < textureTransform.info.scale.tracks.size()) { const Model::ComplexModel::AnimationTrack& track = textureTransform.info.scale.tracks[sequenceID]; f32 progress = scaleBoneInstance->currentAnimation.progress; - if (textureTransform.info.scale.globalLoopIndex != -1) + if (isScaleGlobalLoop) { const AnimationGlobalLoopInstance& globalLoop = instance.globalLoops[textureTransform.info.scale.globalLoopIndex]; progress = globalLoop.currentTime; @@ -1266,84 +1351,74 @@ namespace Animation // Transition Sequence { - - if (translationBoneInstance) + if (translationBoneInstance && !isTranslationGlobalLoop) { f32 timeToTransition = translationBoneInstance->timeToTransition; f32 transitionDuration = translationBoneInstance->transitionDuration; f32 transitionProgress = timeToTransition ? glm::clamp(1.0f - (timeToTransition / transitionDuration), 0.0f, 1.0f) : 1.0f; - u32 sequenceID = translationBoneInstance->nextAnimation.sequenceID; - if (sequenceID < textureTransform.info.translation.tracks.size()) + if (transitionDuration > 0.0f) { - const Model::ComplexModel::AnimationTrack& track = textureTransform.info.translation.tracks[sequenceID]; - - if (track.values.size() > 0 && track.timestamps.size() > 0) + u32 sequenceID = translationBoneInstance->nextAnimation.sequenceID; + if (sequenceID < textureTransform.info.translation.tracks.size()) { - f32 progress = translationBoneInstance->nextAnimation.progress; + const Model::ComplexModel::AnimationTrack& track = textureTransform.info.translation.tracks[sequenceID]; - if (textureTransform.info.translation.globalLoopIndex != -1) + if (track.values.size() > 0 && track.timestamps.size() > 0) { - const AnimationGlobalLoopInstance& globalLoop = instance.globalLoops[textureTransform.info.translation.globalLoopIndex]; - progress = globalLoop.currentTime; - } + f32 progress = translationBoneInstance->nextAnimation.progress; - vec3 translation = InterpolateKeyframe(track, progress); - translationValue = glm::mix(translationValue, translation, transitionProgress); + vec3 translation = InterpolateKeyframe(track, progress); + translationValue = glm::mix(translationValue, translation, transitionProgress); + } } } } - if (rotationBoneInstance) + if (rotationBoneInstance && !isRotationGlobalLoop) { f32 timeToTransition = rotationBoneInstance->timeToTransition; f32 transitionDuration = rotationBoneInstance->transitionDuration; f32 transitionProgress = timeToTransition ? glm::clamp(1.0f - (timeToTransition / transitionDuration), 0.0f, 1.0f) : 1.0f; - u32 sequenceID = rotationBoneInstance->nextAnimation.sequenceID; - if (sequenceID < textureTransform.info.rotation.tracks.size()) + if (transitionDuration > 0.0f) { - const Model::ComplexModel::AnimationTrack& track = textureTransform.info.rotation.tracks[sequenceID]; - - if (track.values.size() > 0 && track.timestamps.size() > 0) + u32 sequenceID = rotationBoneInstance->nextAnimation.sequenceID; + if (sequenceID < textureTransform.info.rotation.tracks.size()) { - f32 progress = rotationBoneInstance->nextAnimation.progress; + const Model::ComplexModel::AnimationTrack& track = textureTransform.info.rotation.tracks[sequenceID]; - if (textureTransform.info.rotation.globalLoopIndex != -1) + if (track.values.size() > 0 && track.timestamps.size() > 0) { - const AnimationGlobalLoopInstance& globalLoop = instance.globalLoops[textureTransform.info.rotation.globalLoopIndex]; - progress = globalLoop.currentTime; - } + f32 progress = rotationBoneInstance->nextAnimation.progress; - quat rotation = InterpolateKeyframe(track, progress); - rotationValue = glm::mix(rotationValue, rotation, transitionProgress); + quat rotation = InterpolateKeyframe(track, progress); + rotationValue = glm::mix(rotationValue, rotation, transitionProgress); + } } } } - if (scaleBoneInstance) + if (scaleBoneInstance && !isScaleGlobalLoop) { f32 timeToTransition = scaleBoneInstance->timeToTransition; f32 transitionDuration = scaleBoneInstance->transitionDuration; f32 transitionProgress = timeToTransition ? glm::clamp(1.0f - (timeToTransition / transitionDuration), 0.0f, 1.0f) : 1.0f; - u32 sequenceID = scaleBoneInstance->nextAnimation.sequenceID; - if (sequenceID < textureTransform.info.scale.tracks.size()) + if (transitionDuration > 0.0f) { - const Model::ComplexModel::AnimationTrack& track = textureTransform.info.scale.tracks[sequenceID]; - - if (track.values.size() > 0 && track.timestamps.size() > 0) + u32 sequenceID = scaleBoneInstance->nextAnimation.sequenceID; + if (sequenceID < textureTransform.info.scale.tracks.size()) { - f32 progress = scaleBoneInstance->nextAnimation.progress; + const Model::ComplexModel::AnimationTrack& track = textureTransform.info.scale.tracks[sequenceID]; - if (textureTransform.info.scale.globalLoopIndex != -1) + if (track.values.size() > 0 && track.timestamps.size() > 0) { - const AnimationGlobalLoopInstance& globalLoop = instance.globalLoops[textureTransform.info.scale.globalLoopIndex]; - progress = globalLoop.currentTime; - } + f32 progress = scaleBoneInstance->nextAnimation.progress; - vec3 scale = InterpolateKeyframe(track, progress); - scaleValue = glm::mix(scaleValue, scale, transitionProgress); + vec3 scale = InterpolateKeyframe(track, progress); + scaleValue = glm::mix(scaleValue, scale, transitionProgress); + } } } } diff --git a/Source/Game/Game/Animation/AnimationSystem.h b/Source/Game/Game/Animation/AnimationSystem.h index 467e5f06..e3998ba3 100644 --- a/Source/Game/Game/Animation/AnimationSystem.h +++ b/Source/Game/Game/Animation/AnimationSystem.h @@ -1482,7 +1482,7 @@ namespace Animation std::vector textureTransforms; robin_hood::unordered_map animationIDToFirstSequenceID; - robin_hood::unordered_map keyBoneIDToBoneID; + robin_hood::unordered_map keyBoneIDToBoneIndex; }; enum class AnimationPlayState : u32 @@ -1562,14 +1562,17 @@ namespace Animation { public: AnimationSystem(ModelRenderer* modelRenderer); + + bool IsEnabled(); bool HasSkeleton(ModelID modelID) { return _storage.skeletons.contains(modelID); } bool AddSkeleton(ModelID modelID, Model::ComplexModel& model); bool HasInstance(InstanceID instanceID) { return _storage.instanceIDToData.contains(instanceID); } bool AddInstance(ModelID modelID, InstanceID instanceID); + bool RemoveInstance(InstanceID instanceID); - bool GetCurrentAnimation(InstanceID instanceID, Bone bone, Type* primary, Type* sequence = nullptr); + bool GetCurrentAnimation(InstanceID instanceID, Bone bone, AnimationSequenceState* primary, AnimationSequenceState* sequence = nullptr); bool IsPlaying(InstanceID instanceID, Bone bone, Type animationID); u32 GetSequenceIDForAnimationID(ModelID modelID, Type animationID); diff --git a/Source/Game/Game/Application/Application.cpp b/Source/Game/Game/Application/Application.cpp index 4fdcec8f..3d6ef1e1 100644 --- a/Source/Game/Game/Application/Application.cpp +++ b/Source/Game/Game/Application/Application.cpp @@ -6,7 +6,9 @@ #include "Game/ECS/Singletons/ClientDBCollection.h" #include "Game/ECS/Singletons/EngineStats.h" #include "Game/ECS/Singletons/MapDB.h" +#include "Game/ECS/Singletons/NetworkState.h" #include "Game/ECS/Singletons/RenderState.h" +#include "Game/ECS/Util/CharSectionUtil.h" #include "Game/ECS/Util/MapUtil.h" #include "Game/Editor/EditorHandler.h" #include "Game/Gameplay/GameConsole/GameConsole.h" @@ -25,6 +27,8 @@ #include #include +#include + #include #include #include @@ -86,6 +90,14 @@ void Application::Stop() void Application::Cleanup() { + entt::registry* registry = ServiceLocator::GetEnttRegistries()->gameRegistry; + entt::registry::context& ctx = registry->ctx(); + auto& networkState = ctx.get(); + if (networkState.client && networkState.client->IsConnected()) + { + networkState.client->Close(); + } + i32 clientDBSaveMethod = CVAR_ClientDBSaveMethod.Get(); if (clientDBSaveMethod == 2) { @@ -117,10 +129,10 @@ void Application::Run() entt::registry* registry = ServiceLocator::GetEnttRegistries()->gameRegistry; entt::registry::context& ctx = registry->ctx(); - ECS::Singletons::RenderState& renderState = ctx.get(); - ECS::Singletons::EngineStats& engineStats = ctx.get(); + auto& renderState = ctx.get(); + auto& engineStats = ctx.get(); - ECS::Singletons::FrameTimes timings; + ECS::Singletons::FrameTimes timings; while (true) { f32 deltaTime = timer.GetDeltaTime(); @@ -263,7 +275,7 @@ bool Application::Init() using namespace ECS::Singletons; entt::registry::context& ctx = _registries.gameRegistry->ctx(); - ClientDBCollection& clientDBCollection = ctx.get(); + auto& clientDBCollection = ctx.get(); clientDBCollection.Register(ClientDBHash::Map, "Map"); clientDBCollection.Register(ClientDBHash::LiquidObject, "LiquidObject"); @@ -273,6 +285,11 @@ bool Application::Init() clientDBCollection.Register(ClientDBHash::CinematicSequence, "CinematicSequence"); clientDBCollection.Register(ClientDBHash::CameraSave, "CameraSave"); clientDBCollection.Register(ClientDBHash::Cursor, "Cursor"); + clientDBCollection.Register(ClientDBHash::AnimationData, "AnimationData"); + clientDBCollection.Register(ClientDBHash::CreatureDisplayInfo, "CreatureDisplayInfo"); + clientDBCollection.Register(ClientDBHash::CreatureDisplayInfoExtra, "CreatureDisplayInfoExtra"); + clientDBCollection.Register(ClientDBHash::CreatureModelData, "CreatureModelData"); + clientDBCollection.Register(ClientDBHash::CharSection, "CharSection"); _registries.gameRegistry->ctx().emplace(); ECS::Util::Map::Refresh(); @@ -379,6 +396,8 @@ bool Application::Init() cursors->SetDirty(); } } + + ECS::Util::CharSection::RefreshData(*_registries.gameRegistry); } // Init AnimationSystem diff --git a/Source/Game/Game/ECS/Components/CastInfo.h b/Source/Game/Game/ECS/Components/CastInfo.h new file mode 100644 index 00000000..214f1fed --- /dev/null +++ b/Source/Game/Game/ECS/Components/CastInfo.h @@ -0,0 +1,15 @@ +#pragma once +#include + +#include + +namespace ECS::Components +{ + struct CastInfo + { + entt::entity target; + + f32 castTime = 0.0f; + f32 duration = 0.0f; + }; +} \ No newline at end of file diff --git a/Source/Game/Game/ECS/Components/DisplayInfo.h b/Source/Game/Game/ECS/Components/DisplayInfo.h new file mode 100644 index 00000000..72545b79 --- /dev/null +++ b/Source/Game/Game/ECS/Components/DisplayInfo.h @@ -0,0 +1,11 @@ +#pragma once +#include + +namespace ECS::Components +{ + struct DisplayInfo + { + public: + u32 displayID; + }; +} \ No newline at end of file diff --git a/Source/Game/Game/ECS/Components/MovementInfo.h b/Source/Game/Game/ECS/Components/MovementInfo.h new file mode 100644 index 00000000..327427f3 --- /dev/null +++ b/Source/Game/Game/ECS/Components/MovementInfo.h @@ -0,0 +1,42 @@ +#pragma once + +#include + +namespace ECS::Components +{ + struct MovementFlags + { + u32 forward : 1 = 0; + u32 backward : 1 = 0; + u32 left : 1 = 0; + u32 right : 1 = 0; + u32 grounded : 1 = 0; + u32 jumping : 1 = 0; + u32 justGrounded : 1 = 0; + u32 justEndedJump : 1 = 0; + }; + + enum class JumpState : u8 + { + None, + Begin, + Jumping, + Fall + }; + + struct MovementInfo + { + f32 pitch = 0.0f; + f32 yaw = 0.0f; + + f32 speed = 7.1111f; + f32 jumpSpeed = 7.9555f; + f32 gravityModifier = 1.0f; + + vec2 horizontalVelocity = vec2(0.0f); + f32 verticalVelocity = 0.0f; + + MovementFlags movementFlags; + JumpState jumpState = JumpState::None; + }; +} \ No newline at end of file diff --git a/Source/Game/Game/ECS/Components/NetworkedEntity.h b/Source/Game/Game/ECS/Components/NetworkedEntity.h new file mode 100644 index 00000000..ac599e40 --- /dev/null +++ b/Source/Game/Game/ECS/Components/NetworkedEntity.h @@ -0,0 +1,25 @@ +#pragma once +#include + +#include + +namespace ECS::Components +{ + struct NetworkedEntity + { + entt::entity networkID; + entt::entity targetEntity; + u32 bodyID = std::numeric_limits().max(); + + vec3 initialPosition = vec3(0.0f); + vec3 desiredPosition = vec3(0.0f); + f32 positionProgress = -1.0f; + + bool positionOrRotationChanged = false; + + // These rotation offsets are specifically for Y axis only + vec4 spineRotationSettings = vec4(0.0f); // x = current, y = target, z = time to change, w = time since last change + vec4 headRotationSettings = vec4(0.0f); // x = current, y = target, z = time to change, w = time since last change + vec4 waistRotationSettings = vec4(0.0f); // x = current, y = target, z = time to change, w = time since last change + }; +} \ No newline at end of file diff --git a/Source/Game/Game/ECS/Components/UnitStatsComponent.h b/Source/Game/Game/ECS/Components/UnitStatsComponent.h new file mode 100644 index 00000000..b60bd5dc --- /dev/null +++ b/Source/Game/Game/ECS/Components/UnitStatsComponent.h @@ -0,0 +1,55 @@ +#pragma once +#include + +namespace ECS::Components +{ + enum class PowerType + { + Health = -2, // This is strictly used for spells + Mana = 0, + Rage, + Focus, + Energy, + Happiness, + Count + }; + + enum class StatType + { + Strength, + Agility, + Stamina, + Intellect, + Spirit, + Count + }; + + enum class ResistanceType + { + Normal, + Holy, + Fire, + Nature, + Frost, + Shadow, + Arcane, + Count + }; + + struct UnitStatsComponent + { + f32 baseHealth; + f32 currentHealth; + f32 maxHealth; + + f32 basePower[(u32)PowerType::Count]; + f32 currentPower[(u32)PowerType::Count]; + f32 maxPower[(u32)PowerType::Count]; + + i32 baseStat[(u32)StatType::Count]; + i32 currentStat[(u32)StatType::Count]; + + i32 baseResistance[(u32)ResistanceType::Count]; + i32 currentResistance[(u32)ResistanceType::Count]; + }; +} \ No newline at end of file diff --git a/Source/Game/Game/ECS/Scheduler.cpp b/Source/Game/Game/ECS/Scheduler.cpp index 6e28975e..aee966d0 100644 --- a/Source/Game/Game/ECS/Scheduler.cpp +++ b/Source/Game/Game/ECS/Scheduler.cpp @@ -11,6 +11,7 @@ #include "Game/ECS/Systems/FreeflyingCamera.h" #include "Game/ECS/Systems/OrbitalCamera.h" #include "Game/ECS/Systems/NetworkConnection.h" +#include "Game/ECS/Systems/UpdateNetworkedEntity.h" #include "Game/ECS/Systems/UpdatePhysics.h" #include "Game/ECS/Systems/UpdateScripts.h" #include @@ -32,6 +33,7 @@ namespace ECS void Scheduler::Init(entt::registry& registry) { Systems::NetworkConnection::Init(registry); + Systems::UpdateNetworkedEntity::Init(registry); Systems::UpdatePhysics::Init(registry); Systems::DrawDebugMesh::Init(registry); Systems::FreeflyingCamera::Init(registry); @@ -51,6 +53,7 @@ namespace ECS Systems::UpdateAABBs::Update(registry, deltaTime); Systems::NetworkConnection::Update(registry, deltaTime); + Systems::UpdateNetworkedEntity::Update(registry, deltaTime); Systems::CharacterController::Update(registry, deltaTime); Systems::FreeflyingCamera::Update(registry, deltaTime); Systems::OrbitalCamera::Update(registry, deltaTime); diff --git a/Source/Game/Game/ECS/Singletons/CameraSaveDB.h b/Source/Game/Game/ECS/Singletons/CameraSaveDB.h index 979506e9..b952af3a 100644 --- a/Source/Game/Game/ECS/Singletons/CameraSaveDB.h +++ b/Source/Game/Game/ECS/Singletons/CameraSaveDB.h @@ -22,7 +22,7 @@ namespace ECS::Singletons entt::registry* registry = ServiceLocator::GetEnttRegistries()->gameRegistry; entt::registry::context& ctx = registry->ctx(); - ClientDBCollection& clientDBCollection = ctx.get(); + auto& clientDBCollection = ctx.get(); auto* cameraSaves = clientDBCollection.Get(ClientDBHash::CameraSave); cameraSaveNameHashToID.clear(); diff --git a/Source/Game/Game/ECS/Singletons/CharSectionSingleton.h b/Source/Game/Game/ECS/Singletons/CharSectionSingleton.h new file mode 100644 index 00000000..5387ef38 --- /dev/null +++ b/Source/Game/Game/ECS/Singletons/CharSectionSingleton.h @@ -0,0 +1,23 @@ +#pragma once +#include + +#include + +namespace ECS::Singletons +{ + struct CharSectionSingleton + { + public: + /* + -- Key Structure -- + raceID -- 4 bits + sexID -- 1 bit + baseSection -- 4 bits + varationIndex -- 5 bits + colorIndex -- 5 bits + flags -- 5 bits + */ + + robin_hood::unordered_map keyToCharSectionID; + }; +} \ No newline at end of file diff --git a/Source/Game/Game/ECS/Singletons/CharacterSingleton.h b/Source/Game/Game/ECS/Singletons/CharacterSingleton.h index 9c5abb13..4ecec312 100644 --- a/Source/Game/Game/ECS/Singletons/CharacterSingleton.h +++ b/Source/Game/Game/ECS/Singletons/CharacterSingleton.h @@ -12,42 +12,24 @@ namespace Animation } namespace ECS::Singletons { - enum class JumpState - { - None, - Begin, - Fall, - End - }; struct CharacterSingleton { public: entt::entity entity; entt::entity modelEntity; + entt::entity targetEntity; JPH::CharacterVirtual* character = nullptr; KeybindGroup* keybindGroup = nullptr; KeybindGroup* cameraToggleKeybindGroup = nullptr; - f32 yaw = 0.0f; - f32 pitch = 0.0f; + bool canControlInAir = true; + bool positionOrRotationIsDirty = false; + f32 positionOrRotationUpdateTimer = 0.0f; // These rotation offsets are specifically for Y axis only vec4 spineRotationSettings = vec4(0.0f); // x = current, y = target, z = time to change, w = time since last change vec4 headRotationSettings = vec4(0.0f); // x = current, y = target, z = time to change, w = time since last change vec4 waistRotationSettings = vec4(0.0f); // x = current, y = target, z = time to change, w = time since last change - - f32 speed = 7.1111f; - JumpState jumpState = JumpState::None; - - struct MovementFlags - { - u32 forward : 1; - u32 backward : 1; - u32 left : 1; - u32 right : 1; - }; - - MovementFlags movementFlags; }; } \ No newline at end of file diff --git a/Source/Game/Game/ECS/Singletons/ClientDBCollection.h b/Source/Game/Game/ECS/Singletons/ClientDBCollection.h index 99062f80..39bed3d0 100644 --- a/Source/Game/Game/ECS/Singletons/ClientDBCollection.h +++ b/Source/Game/Game/ECS/Singletons/ClientDBCollection.h @@ -20,14 +20,19 @@ namespace ECS::Singletons enum class ClientDBHash : u32 { - Map = GetHash("Map.cdb"_h), - LiquidObject = GetHash("LiquidObject.cdb"_h), - LiquidType = GetHash("LiquidType.cdb"_h), - LiquidMaterial = GetHash("LiquidMaterial.cdb"_h), - CinematicCamera = GetHash("CinematicCamera.cdb"_h), - CinematicSequence = GetHash("CinematicSequence.cdb"_h), - CameraSave = GetHash("CameraSave.cdb"_h), - Cursor = GetHash("Cursor.cdb"_h) + Map = GetHash("Map.cdb"_h), + LiquidObject = GetHash("LiquidObject.cdb"_h), + LiquidType = GetHash("LiquidType.cdb"_h), + LiquidMaterial = GetHash("LiquidMaterial.cdb"_h), + CinematicCamera = GetHash("CinematicCamera.cdb"_h), + CinematicSequence = GetHash("CinematicSequence.cdb"_h), + CameraSave = GetHash("CameraSave.cdb"_h), + Cursor = GetHash("Cursor.cdb"_h), + AnimationData = GetHash("AnimationData.cdb"_h), + CreatureDisplayInfo = GetHash("CreatureDisplayInfo.cdb"_h), + CreatureDisplayInfoExtra = GetHash("CreatureDisplayInfoExtra.cdb"_h), + CreatureModelData = GetHash("CreatureModelData.cdb"_h), + CharSection = GetHash("CharSection.cdb"_h), }; struct ClientDBCollection diff --git a/Source/Game/Game/ECS/Singletons/NetworkState.h b/Source/Game/Game/ECS/Singletons/NetworkState.h index c79b2157..f3281f70 100644 --- a/Source/Game/Game/ECS/Singletons/NetworkState.h +++ b/Source/Game/Game/ECS/Singletons/NetworkState.h @@ -1,4 +1,6 @@ #pragma once +#include + #include namespace Network @@ -12,7 +14,12 @@ namespace ECS::Singletons struct NetworkState { public: + std::string characterName = "dev"; + std::unique_ptr client; std::unique_ptr packetHandler; + + robin_hood::unordered_map networkIDToEntity; + robin_hood::unordered_map entityToNetworkID; }; } \ No newline at end of file diff --git a/Source/Game/Game/ECS/Systems/CalculateCameraMatrices.cpp b/Source/Game/Game/ECS/Systems/CalculateCameraMatrices.cpp index 73613039..3b36e7da 100644 --- a/Source/Game/Game/ECS/Systems/CalculateCameraMatrices.cpp +++ b/Source/Game/Game/ECS/Systems/CalculateCameraMatrices.cpp @@ -16,7 +16,7 @@ AutoCVar_Int CVAR_CameraLockCullingFrustum(CVarCategory::Client | CVarCategory:: namespace ECS::Systems { - vec4 EncodePlane(vec3 position, vec3 normal) + inline vec4 EncodePlane(vec3 position, vec3 normal) { vec3 normalizedNormal = glm::normalize(normal); vec4 result = vec4(normalizedNormal, glm::dot(normalizedNormal, position)); @@ -29,7 +29,7 @@ namespace ECS::Systems RenderResources& renderResources = gameRenderer->GetRenderResources(); entt::registry::context& ctx = registry.ctx(); - ECS::Singletons::ActiveCamera& activeCamera = ctx.get(); + auto& activeCamera = ctx.get(); auto view = registry.view(); view.each([&](entt::entity e, Components::Transform& transform, Components::Camera& camera) @@ -41,11 +41,11 @@ namespace ECS::Systems } vec2 renderSize = gameRenderer->GetRenderer()->GetRenderSize(); - camera.aspectRatio = renderSize.x / renderSize.y; + camera.aspectRatio = static_cast(Renderer::Settings::SCREEN_WIDTH) / static_cast(Renderer::Settings::SCREEN_HEIGHT); { - f32 diagonalFov = glm::radians(camera.fov) / sqrt(1.0f + (camera.aspectRatio * camera.aspectRatio)); - camera.viewToClip = glm::perspective(diagonalFov, camera.aspectRatio, camera.farClip, camera.nearClip); + f32 fov = glm::radians(camera.fov) * 0.6f; + camera.viewToClip = glm::perspective(fov, camera.aspectRatio, camera.farClip, camera.nearClip); camera.clipToView = glm::inverse(camera.viewToClip); } @@ -92,7 +92,7 @@ namespace ECS::Systems Right = glm::vec3(transformMatrix * glm::vec4(Right, 0.f)); Up = glm::vec3(transformMatrix * glm::vec4(Up, 0.f)); - f32 fov = glm::radians(camera.fov) / sqrt(1.0f + (camera.aspectRatio * camera.aspectRatio)); + f32 fov = glm::radians(camera.fov) * 0.6f; const float halfVSide = camera.farClip * tanf(fov * .5f); const float halfHSide = halfVSide * camera.aspectRatio; const glm::vec3 frontMultFar = camera.farClip * Front; diff --git a/Source/Game/Game/ECS/Systems/CalculateShadowCameraMatrices.cpp b/Source/Game/Game/ECS/Systems/CalculateShadowCameraMatrices.cpp index c3404464..9768b0fb 100644 --- a/Source/Game/Game/ECS/Systems/CalculateShadowCameraMatrices.cpp +++ b/Source/Game/Game/ECS/Systems/CalculateShadowCameraMatrices.cpp @@ -24,7 +24,7 @@ AutoCVar_Int CVAR_ShadowCascadeTextureSize(CVarCategory::Client | CVarCategory:: namespace ECS::Systems { - vec4 EncodePlane(vec3 position, vec3 normal) + inline vec4 EncodePlane(vec3 position, vec3 normal) { vec3 normalizedNormal = glm::normalize(normal); vec4 result = vec4(normalizedNormal, glm::dot(normalizedNormal, position)); @@ -68,10 +68,10 @@ namespace ECS::Systems // Get active render camera entt::registry::context& ctx = registry.ctx(); - ECS::Singletons::ActiveCamera& activeCamera = ctx.get(); + auto& activeCamera = ctx.get(); - ECS::Components::Transform& cameraTransform = registry.get(activeCamera.entity); - ECS::Components::Camera& camera = registry.get(activeCamera.entity); + auto& cameraTransform = registry.get(activeCamera.entity); + auto& camera = registry.get(activeCamera.entity); vec3 cameraPos = cameraTransform.GetWorldPosition(); const mat4x4& cameraViewProj = camera.worldToClip; diff --git a/Source/Game/Game/ECS/Systems/CharacterController.cpp b/Source/Game/Game/ECS/Systems/CharacterController.cpp index ce565d12..684e593c 100644 --- a/Source/Game/Game/ECS/Systems/CharacterController.cpp +++ b/Source/Game/Game/ECS/Systems/CharacterController.cpp @@ -3,25 +3,36 @@ #include "Game/Animation/AnimationSystem.h" #include "Game/ECS/Components/AABB.h" #include "Game/ECS/Components/Camera.h" +#include "Game/ECS/Components/DisplayInfo.h" #include "Game/ECS/Components/Events.h" #include "Game/ECS/Components/Model.h" +#include "Game/ECS/Components/MovementInfo.h" #include "Game/ECS/Components/Name.h" +#include "Game/ECS/Components/NetworkedEntity.h" +#include "Game/ECS/Components/UnitStatsComponent.h" #include "Game/ECS/Singletons/ActiveCamera.h" #include "Game/ECS/Singletons/CharacterSingleton.h" #include "Game/ECS/Singletons/FreeflyingCameraSettings.h" #include "Game/ECS/Singletons/JoltState.h" +#include "Game/ECS/Singletons/NetworkState.h" #include "Game/ECS/Singletons/OrbitalCameraSettings.h" #include "Game/ECS/Util/EventUtil.h" #include "Game/ECS/Util/Transforms.h" +#include "Game/Editor/EditorHandler.h" #include "Game/Gameplay/MapLoader.h" #include "Game/Rendering/GameRenderer.h" #include "Game/Rendering/Debug/JoltDebugRenderer.h" #include "Game/Rendering/Model/ModelLoader.h" +#include "Game/Util/CharacterControllerUtil.h" +#include "Game/Util/PhysicsUtil.h" +#include "Game/Util/UnitUtil.h" #include "Game/Util/ServiceLocator.h" #include #include +#include + #include #include #include @@ -55,6 +66,14 @@ namespace ECS::Systems registry.emplace(characterSingleton.modelEntity); registry.emplace(characterSingleton.modelEntity); + registry.emplace(characterSingleton.modelEntity); + auto& displayInfo = registry.emplace(characterSingleton.modelEntity); + displayInfo.displayID = 50; + + auto& unitStatsComponent = registry.emplace(characterSingleton.modelEntity); + unitStatsComponent.currentHealth = 50.0f; + unitStatsComponent.maxHealth = 100.0f; + transformSystem.SetWorldPosition(characterSingleton.entity, vec3(0.0f, 0.0f, 0.0f)); transformSystem.SetWorldPosition(characterSingleton.modelEntity, vec3(0.0f, 0.0f, 0.0f)); transformSystem.ParentEntityTo(characterSingleton.entity, characterSingleton.modelEntity); @@ -71,16 +90,57 @@ namespace ECS::Systems characterSingleton.keybindGroup->AddKeyboardCallback("Left", GLFW_KEY_A, KeybindAction::Press, KeybindModifier::Any, nullptr); characterSingleton.keybindGroup->AddKeyboardCallback("Right", GLFW_KEY_D, KeybindAction::Press, KeybindModifier::Any, nullptr); characterSingleton.keybindGroup->AddKeyboardCallback("Upwards", GLFW_KEY_SPACE, KeybindAction::Press, KeybindModifier::Any, nullptr); + characterSingleton.keybindGroup->AddKeyboardCallback("Select Target", GLFW_MOUSE_BUTTON_LEFT, KeybindAction::Release, KeybindModifier::Any, [&](i32 key, KeybindAction action, KeybindModifier modifier) + { + entt::registry* registry = ServiceLocator::GetEnttRegistries()->gameRegistry; + + entt::entity targetEntity = entt::null; + if (::Util::Physics::GetEntityAtMousePosition(ServiceLocator::GetEditorHandler()->GetViewport(), targetEntity)) + { + if (targetEntity != characterSingleton.entity) + { + if (registry->valid(targetEntity) && !registry->all_of(targetEntity)) + { + targetEntity = entt::null; + } + } + } + + if (characterSingleton.targetEntity == targetEntity) + return false; + + Singletons::NetworkState& networkState = registry->ctx().get(); + if (networkState.client && networkState.client->IsConnected()) + { + std::shared_ptr buffer = Bytebuffer::Borrow<128>(); + Network::PacketHeader header = + { + .opcode = Network::Opcode::MSG_ENTITY_TARGET_UPDATE, + .size = sizeof(entt::entity) + }; + + entt::entity targetNetworkID = networkState.entityToNetworkID[targetEntity]; + buffer->Put(header); + buffer->Put(targetNetworkID); + + networkState.client->Send(buffer); + } + + characterSingleton.targetEntity = targetEntity; + return true; + }); + + characterSingleton.cameraToggleKeybindGroup->SetActive(true); characterSingleton.cameraToggleKeybindGroup->AddKeyboardCallback("Toggle Camera Mode", GLFW_KEY_C, KeybindAction::Press, KeybindModifier::Any, [](i32 key, KeybindAction action, KeybindModifier modifier) { entt::registry* registry = ServiceLocator::GetEnttRegistries()->gameRegistry; entt::registry::context& ctx = registry->ctx(); - Singletons::ActiveCamera& activeCamera = ctx.get(); - Singletons::CharacterSingleton& characterSingleton = ctx.get(); - Singletons::FreeflyingCameraSettings& freeFlyingCameraSettings = ctx.get(); - Singletons::OrbitalCameraSettings& orbitalCameraSettings = ctx.get(); + auto& activeCamera = ctx.get(); + auto& characterSingleton = ctx.get(); + auto& freeFlyingCameraSettings = ctx.get(); + auto& orbitalCameraSettings = ctx.get(); InputManager* inputManager = ServiceLocator::GetInputManager(); @@ -92,16 +152,16 @@ namespace ECS::Systems if (!registry->valid(freeFlyingCameraSettings.entity)) return false; - ECS::Util::CameraUtil::SetCaptureMouse(false); // Uncapture mouse for Orbital Camera when switching to FreeFlying Camera + Util::CameraUtil::SetCaptureMouse(false); // Uncapture mouse for Orbital Camera when switching to FreeFlying Camera activeCamera.entity = freeFlyingCameraSettings.entity; characterSingleton.keybindGroup->SetActive(false); orbitalCameraKeybindGroup->SetActive(false); freeFlyingCameraKeybindGroup->SetActive(true); - ECS::Util::CameraUtil::SetCaptureMouse(false); // Uncapture mouse for FreeFlying Camera when switching to FreeFlying Camera + Util::CameraUtil::SetCaptureMouse(false); // Uncapture mouse for FreeFlying Camera when switching to FreeFlying Camera - auto& camera = registry->get(activeCamera.entity); + auto& camera = registry->get(activeCamera.entity); camera.dirtyView = true; camera.dirtyPerspective = true; } @@ -112,11 +172,11 @@ namespace ECS::Systems TransformSystem& transformSystem = ctx.get(); - ECS::Components::Transform& transform = registry->get(characterSingleton.entity); + auto& transform = registry->get(characterSingleton.entity); transformSystem.ParentEntityTo(characterSingleton.entity, orbitalCameraSettings.entity); transformSystem.SetLocalPosition(orbitalCameraSettings.entity, orbitalCameraSettings.cameraCurrentZoomOffset); - ECS::Util::CameraUtil::SetCaptureMouse(false); // Uncapture mouse for FreeFlyingCamera when switching to orbital camera + Util::CameraUtil::SetCaptureMouse(false); // Uncapture mouse for FreeFlyingCamera when switching to orbital camera activeCamera.entity = orbitalCameraSettings.entity; orbitalCameraSettings.entityToTrack = characterSingleton.entity; @@ -124,83 +184,45 @@ namespace ECS::Systems characterSingleton.keybindGroup->SetActive(true); orbitalCameraKeybindGroup->SetActive(true); - ECS::Util::CameraUtil::SetCaptureMouse(false); // Uncapture mouse for Orbital Camera when switching to orbital camer + Util::CameraUtil::SetCaptureMouse(false); // Uncapture mouse for Orbital Camera when switching to orbital camer - auto& camera = registry->get(activeCamera.entity); + auto& camera = registry->get(activeCamera.entity); camera.dirtyView = true; camera.dirtyPerspective = true; } return true; }); - characterSingleton.cameraToggleKeybindGroup->AddKeyboardCallback("Move Character To Camera", GLFW_KEY_G, KeybindAction::Press, KeybindModifier::Any, [](i32 key, KeybindAction action, KeybindModifier modifier) { entt::registry* registry = ServiceLocator::GetEnttRegistries()->gameRegistry; entt::registry::context& ctx = registry->ctx(); - Singletons::CharacterSingleton& characterSingleton = ctx.get(); - Singletons::ActiveCamera& activeCamera = ctx.get(); - Singletons::FreeflyingCameraSettings& freeFlyingCameraSettings = ctx.get(); + auto& characterSingleton = ctx.get(); + auto& activeCamera = ctx.get(); + auto& freeFlyingCameraSettings = ctx.get(); if (activeCamera.entity != freeFlyingCameraSettings.entity) return false; TransformSystem& transformSystem = ctx.get(); - ECS::Components::Camera& camera = registry->get(freeFlyingCameraSettings.entity); - ECS::Components::Transform& cameraTransform = registry->get(freeFlyingCameraSettings.entity); - ECS::Components::Transform& characterTransform = registry->get(characterSingleton.entity); + auto& camera = registry->get(freeFlyingCameraSettings.entity); + auto& cameraTransform = registry->get(freeFlyingCameraSettings.entity); + auto& characterTransform = registry->get(characterSingleton.entity); + auto& characterMovementInfo = registry->get(characterSingleton.modelEntity); vec3 newPosition = cameraTransform.GetWorldPosition(); characterSingleton.character->SetLinearVelocity(JPH::Vec3::sZero()); characterSingleton.character->SetPosition(JPH::RVec3Arg(newPosition.x, newPosition.y, newPosition.z)); transformSystem.SetWorldPosition(characterSingleton.entity, newPosition); - characterSingleton.pitch = 0.0f; - characterSingleton.yaw = glm::pi() + glm::radians(camera.yaw); + characterMovementInfo.pitch = 0.0f; + characterMovementInfo.yaw = glm::pi() + glm::radians(camera.yaw); + characterSingleton.positionOrRotationIsDirty = true; return true; }); - - characterSingleton.cameraToggleKeybindGroup->SetActive(true); - } - - void OnJumpStartFinished(u32 instanceID, Animation::Type animation, Animation::Type interruptedBy) - { - if (animation != Animation::Type::JumpStart) - return; - - entt::registry* registry = ServiceLocator::GetEnttRegistries()->gameRegistry; - auto& characterSingleton = registry->ctx().get(); - - if (characterSingleton.jumpState != ECS::Singletons::JumpState::Begin) - return; - - bool wasCancelled = interruptedBy != Animation::Type::Invalid && interruptedBy != Animation::Type::Jump && interruptedBy != Animation::Type::JumpEnd && interruptedBy != Animation::Type::JumpLandRun; - if (wasCancelled) - { - characterSingleton.jumpState = ECS::Singletons::JumpState::None; - return; - } - - characterSingleton.jumpState = ECS::Singletons::JumpState::Fall; - - Animation::AnimationSystem* animationSystem = ServiceLocator::GetAnimationSystem(); - animationSystem->SetBoneSequence(instanceID, Animation::Bone::Default, Animation::Type::Jump, Animation::Flag::None, Animation::BlendOverride::None); - } - void OnJumpEndFinished(u32 instanceID, Animation::Type animation, Animation::Type interruptedBy) - { - if (animation != Animation::Type::JumpEnd && animation != Animation::Type::JumpLandRun) - return; - - entt::registry* registry = ServiceLocator::GetEnttRegistries()->gameRegistry; - auto& characterSingleton = registry->ctx().get(); - - if (characterSingleton.jumpState != ECS::Singletons::JumpState::End) - return; - - characterSingleton.jumpState = ECS::Singletons::JumpState::None; } void CharacterController::Update(entt::registry& registry, f32 deltaTime) @@ -210,19 +232,18 @@ namespace ECS::Systems entt::registry::context& ctx = registry.ctx(); auto& characterSingleton = ctx.get(); + auto& model = registry.get(characterSingleton.modelEntity); + auto& movementInfo = registry.get(characterSingleton.modelEntity); Util::EventUtil::OnEvent([&](const Components::MapLoadedEvent& event) { InitCharacterController(registry); }); - auto& transformSystem = ctx.get(); - quat characterRotation = quat(vec3(characterSingleton.pitch, characterSingleton.yaw, 0.0f)); - transformSystem.SetWorldRotation(characterSingleton.modelEntity, characterRotation); #ifdef JPH_DEBUG_RENDERER // TODO: Fix Jolt Primitives being erased in JoltDebugRenderer causing crash when changing map - //Components::Transform& transform = registry.get(characterSingleton.modelEntity); + //auto& transform = registry.get(characterSingleton.modelEntity); // //JoltDebugRenderer* joltDebugRenderer = ServiceLocator::GetGameRenderer()->GetJoltDebugRenderer(); // @@ -238,237 +259,262 @@ namespace ECS::Systems //JPH::Vec3 joltModelScale = JPH::Vec3(modelScale.x, modelScale.y, modelScale.z); //characterSingleton.character->GetShape()->Draw(joltDebugRenderer, joltModelMatrix, joltModelScale, JPH::Color::sCyan, true, true); #endif - if (!keybindGroup->IsActive()) return; + auto& transformSystem = ctx.get(); + auto& joltState = ctx.get(); auto& orbitalCameraSettings = ctx.get(); + auto& unitStatsComponent = registry.get(characterSingleton.modelEntity); - static JPH::Vec3 gravity = JPH::Vec3(0.0f, -9.81f, 0.0f); - JPH::Vec3 velocity = JPH::Vec3(0.0f, 0.0f, 0.0f); + static JPH::Vec3 gravity = JPH::Vec3(0.0f, -19.291105f, 0.0f); JPH::Vec3 moveDirection = JPH::Vec3(0.0f, 0.0f, 0.0f); - bool movingForward = keybindGroup->IsKeybindPressed("Forward"_h) || (orbitalCameraSettings.mouseLeftDown && orbitalCameraSettings.mouseRightDown); - bool movingBackward = keybindGroup->IsKeybindPressed("Backward"_h); - bool movingLeft = keybindGroup->IsKeybindPressed("Left"_h); - bool movingRight = keybindGroup->IsKeybindPressed("Right"_h); - - if (movingForward) - moveDirection += JPH::Vec3(0.0f, 0.0f, -1.0f); - - if (movingBackward) - moveDirection += JPH::Vec3(0.0f, 0.0f, 1.0f); - - if (movingLeft) - moveDirection += JPH::Vec3(1.0f, 0.0f, 0.0f); - - if (movingRight) - moveDirection += JPH::Vec3(-1.0f, 0.0f, 0.0f); + bool isInputForwardDown = keybindGroup->IsKeybindPressed("Forward"_h) || (orbitalCameraSettings.mouseLeftDown && orbitalCameraSettings.mouseRightDown); + bool isInputBackwardDown = keybindGroup->IsKeybindPressed("Backward"_h); + bool isInputLeftDown = keybindGroup->IsKeybindPressed("Left"_h); + bool isInputRightDown = keybindGroup->IsKeybindPressed("Right"_h); + + bool isAlive = unitStatsComponent.currentHealth > 0.0f; + bool isMovingForward = (isInputForwardDown && !isInputBackwardDown) * isAlive; + bool isMovingBackward = (isInputBackwardDown && !isInputForwardDown) * isAlive; + bool isMovingLeft = (isInputLeftDown && !isInputRightDown) * isAlive; + bool isMovingRight = (isInputRightDown && !isInputLeftDown) * isAlive; + bool isMoving = isInputForwardDown || isInputBackwardDown || isInputLeftDown || isInputRightDown; + + moveDirection += isMovingForward * JPH::Vec3(0.0f, 0.0f, -1.0f); + moveDirection += isMovingBackward * JPH::Vec3(0.0f, 0.0f, 1.0f); + moveDirection += isMovingLeft * JPH::Vec3(1.0f, 0.0f, 0.0f);; + moveDirection += isMovingRight * JPH::Vec3(-1.0f, 0.0f, 0.0f);;; + + quat characterRotation = quat(vec3(movementInfo.pitch, movementInfo.yaw, 0.0f)); + if (isAlive) + { + transformSystem.SetWorldRotation(characterSingleton.modelEntity, characterRotation); - JPH::Quat virtualCharacterRotation = JPH::Quat(characterRotation.x, characterRotation.y, characterRotation.z, characterRotation.w); - moveDirection = virtualCharacterRotation * moveDirection; + JPH::Quat joltRotation = JPH::Quat(characterRotation.x, characterRotation.y, characterRotation.z, characterRotation.w); + characterSingleton.character->SetRotation(joltRotation); + } - bool moveForward = movingForward && !movingBackward; - bool moveBackward = movingBackward && !movingForward; - bool moveLeft = movingLeft && !movingRight; - bool moveRight = movingRight && !movingLeft; + Components::MovementFlags previousMovementFlags = movementInfo.movementFlags; - characterSingleton.movementFlags.forward = moveForward; - characterSingleton.movementFlags.backward = moveBackward; - characterSingleton.movementFlags.left = moveLeft; - characterSingleton.movementFlags.right = moveRight; + movementInfo.movementFlags.forward = isMovingForward; + movementInfo.movementFlags.backward = isMovingBackward; + movementInfo.movementFlags.left = isMovingLeft; + movementInfo.movementFlags.right = isMovingRight; + movementInfo.movementFlags.justGrounded = false; + movementInfo.movementFlags.justEndedJump = false; JPH::CharacterVirtual::EGroundState groundState = characterSingleton.character->GetGroundState(); - bool isGrounded = groundState == JPH::CharacterVirtual::EGroundState::OnGround; // Fix for animations bricking when turning off animations while jumping state is not None bool animationsEnabled = *CVarSystem::Get()->GetIntCVar(CVarCategory::Client | CVarCategory::Rendering, "animationEnabled"); - if (!animationsEnabled && characterSingleton.jumpState != ECS::Singletons::JumpState::None) + if (!animationsEnabled && movementInfo.jumpState != Components::JumpState::None) { - characterSingleton.jumpState = ECS::Singletons::JumpState::None; + movementInfo.jumpState = Components::JumpState::None; } // TODO : When jumping, we need to incoporate checks from the physics system to handle if jumping ends early - bool isJumping = false; - bool canJump = characterSingleton.jumpState == ECS::Singletons::JumpState::None || characterSingleton.jumpState == ECS::Singletons::JumpState::End; + bool isGrounded = groundState == JPH::CharacterVirtual::EGroundState::OnGround; + bool canJump = isAlive && isGrounded && (!movementInfo.movementFlags.jumping && movementInfo.jumpState == Components::JumpState::None); + bool isTryingToJump = keybindGroup->IsKeybindPressed("Upwards"_h) && canJump; - if (isGrounded) + JPH::Quat virtualCharacterRotation = JPH::Quat(characterRotation.x, characterRotation.y, characterRotation.z, characterRotation.w); + if (!moveDirection.IsNearZero()) { - if (keybindGroup->IsKeybindPressed("Upwards"_h) && canJump) - isJumping = true; + moveDirection = virtualCharacterRotation * moveDirection; + moveDirection = moveDirection.Normalized(); + } - if (moveDirection.LengthSq() > 0.0f) - { - moveDirection = moveDirection.Normalized(); - moveDirection.SetY(isJumping); + f32 speed = movementInfo.speed; + if (isMovingBackward) + speed *= 0.5f; - f32 moveSpeed = characterSingleton.speed; - if (movingBackward) - moveSpeed *= 0.5f; + JPH::Vec3 currentVelocity = characterSingleton.character->GetLinearVelocity(); + JPH::Vec3 desiredVelocity = characterSingleton.character->GetGroundVelocity() + (moveDirection * speed); + JPH::Vec3 newVelocity = JPH::Vec3(0.0f, 0.0f, 0.0f); - moveDirection *= JPH::Vec3(moveSpeed, 6.0f, moveSpeed); - } - else if (isJumping) + if (!desiredVelocity.IsNearZero() || currentVelocity.GetY() < 0.0f || !isGrounded) + { + desiredVelocity.SetY(currentVelocity.GetY()); + } + + bool canControlInAir = isGrounded || characterSingleton.canControlInAir; + characterSingleton.canControlInAir = canControlInAir; + + if (isGrounded || (canControlInAir && isMoving)) + { + if (desiredVelocity.GetY() < 0.0f) { - moveDirection = JPH::Vec3(0.0f, 6.0f, 0.0f); + desiredVelocity.SetY(0.0f); } - velocity = characterSingleton.character->GetGroundVelocity() + moveDirection; + newVelocity = desiredVelocity; - if (groundState == JPH::CharacterVirtual::EGroundState::OnSteepGround) + if (isTryingToJump) { - velocity += deltaTime * gravity; + f32 jumpSpeed = movementInfo.jumpSpeed * movementInfo.gravityModifier; + newVelocity += JPH::Vec3(0.0f, jumpSpeed, 0.0f); + + movementInfo.movementFlags.jumping = true; + movementInfo.jumpState = Components::JumpState::Begin; + } + else + { + characterSingleton.canControlInAir = false; } } else { - velocity = characterSingleton.character->GetLinearVelocity() + deltaTime * gravity; + newVelocity = currentVelocity + ((gravity * movementInfo.gravityModifier) * deltaTime); } - characterSingleton.character->SetLinearVelocity(velocity); + characterSingleton.character->SetLinearVelocity(newVelocity); + + ::Util::CharacterController::UpdateSettings updateSettings = + { + .mStickToFloorStepDown = vec3(0.0f, -0.2f, 0.0f), + .mWalkStairsStepUp = vec3(0.0f, 1.1918f, 0.0f), + .mWalkStairsStepDownExtra = vec3(0.0f, 0.0f, 0.0f) + }; - JPH::CharacterVirtual::ExtendedUpdateSettings updateSettings; JPH::DefaultBroadPhaseLayerFilter broadPhaseLayerFilter(joltState.objectVSBroadPhaseLayerFilter, Jolt::Layers::MOVING); JPH::DefaultObjectLayerFilter objectLayerFilter(joltState.objectVSObjectLayerFilter, Jolt::Layers::MOVING); JPH::BodyFilter bodyFilter; JPH::ShapeFilter shapeFilter; - characterSingleton.character->ExtendedUpdate(deltaTime, gravity, updateSettings, broadPhaseLayerFilter, objectLayerFilter, bodyFilter, shapeFilter, joltState.allocator); + ::Util::CharacterController::Update(characterSingleton.character, deltaTime, gravity, updateSettings, broadPhaseLayerFilter, objectLayerFilter, bodyFilter, shapeFilter, joltState.allocator); + JPH::Vec3 position = characterSingleton.character->GetPosition(); transformSystem.SetWorldPosition(characterSingleton.entity, vec3(position.GetX(), position.GetY(), position.GetZ())); - Animation::AnimationSystem* animationSystem = ServiceLocator::GetAnimationSystem(); - u32 instanceID = registry.get(characterSingleton.modelEntity).instanceID; + JPH::Vec3 linearVelocity = characterSingleton.character->GetLinearVelocity(); + movementInfo.horizontalVelocity = vec2(linearVelocity.GetX(), linearVelocity.GetZ()); + movementInfo.verticalVelocity = linearVelocity.GetY(); - auto SetOrientation = [&](vec4& settings, f32 orientation) - { - f32 currentOrientation = settings.x; - if (orientation == currentOrientation) - return; + groundState = characterSingleton.character->GetGroundState(); - settings.y = orientation; - settings.w = 0.0f; + bool wasGrounded = isGrounded; + isGrounded = groundState == JPH::CharacterVirtual::EGroundState::OnGround; - f32 timeToChange = glm::abs(orientation - currentOrientation) / 450.0f; - timeToChange = glm::max(timeToChange, 0.01f); - settings.z = timeToChange; - }; - auto TryPlayAnimation = [&animationSystem, &characterSingleton, instanceID](Animation::Bone bone, Animation::Type animationID, Animation::Flag flags = Animation::Flag::Loop, Animation::BlendOverride blendOverride = Animation::BlendOverride::Auto, Animation::AnimationCallback callback = nullptr) -> bool + movementInfo.movementFlags.grounded = isGrounded; + if (isGrounded) { - bool canPlay = !animationSystem->IsPlaying(instanceID, bone, animationID); - if (canPlay) + if (!wasGrounded) { - if (!animationSystem->SetBoneSequence(instanceID, bone, animationID, flags, blendOverride, callback)) - return false; + characterSingleton.positionOrRotationIsDirty = true; + movementInfo.movementFlags.justGrounded = true; } - return canPlay; - }; - - if (isJumping) - { - if (TryPlayAnimation(Animation::Bone::Default, Animation::Type::JumpStart, Animation::Flag::None, Animation::BlendOverride::None, OnJumpStartFinished)) + if (movementInfo.movementFlags.jumping || movementInfo.jumpState != Components::JumpState::None) { - characterSingleton.jumpState = ECS::Singletons::JumpState::Begin; + movementInfo.movementFlags.jumping = false; + movementInfo.movementFlags.justEndedJump = true; + movementInfo.jumpState = Components::JumpState::None; } } - Animation::Type currentAnimation = Animation::Type::Invalid; - Animation::Type nextAnimation = Animation::Type::Invalid; - animationSystem->GetCurrentAnimation(instanceID, Animation::Bone::Default, ¤tAnimation, &nextAnimation); + bool wasMoving = previousMovementFlags.forward || previousMovementFlags.backward || previousMovementFlags.left || previousMovementFlags.right; - bool isPlayingJump = characterSingleton.jumpState == ECS::Singletons::JumpState::Begin || characterSingleton.jumpState == ECS::Singletons::JumpState::Fall; - if (!isPlayingJump) + static constexpr f32 positionOrRotationUpdateInterval = 1.0f / 60.0f; + if (characterSingleton.positionOrRotationUpdateTimer >= positionOrRotationUpdateInterval) { - bool canOverrideJumpEnd = true; - if (characterSingleton.jumpState == ECS::Singletons::JumpState::End) + if (!isGrounded || isMoving || wasMoving || characterSingleton.positionOrRotationIsDirty) { - if ((currentAnimation == Animation::Type::JumpLandRun || nextAnimation == Animation::Type::JumpLandRun) && !moveBackward) + // Just started moving + auto& networkState = ctx.get(); + if (networkState.client && networkState.client->IsConnected()) { - canOverrideJumpEnd = false; + auto& transform = registry.get(characterSingleton.modelEntity); + + std::shared_ptr buffer = Bytebuffer::Borrow<128>(); + Network::PacketHeader header = + { + .opcode = Network::Opcode::MSG_ENTITY_MOVE, + .size = sizeof(vec3) + sizeof(quat) + sizeof(Components::MovementFlags) + sizeof(f32) + }; + + buffer->Put(header); + buffer->Put(transform.GetWorldPosition()); + buffer->Put(transform.GetWorldRotation()); + buffer->Put(movementInfo.movementFlags); + buffer->Put(movementInfo.verticalVelocity); + + networkState.client->Send(buffer); } + + characterSingleton.positionOrRotationIsDirty = false; } - if (isGrounded && canOverrideJumpEnd) + characterSingleton.positionOrRotationUpdateTimer -= positionOrRotationUpdateInterval; + } + else + { + if ((isGrounded && !isMoving) && wasMoving) { - Animation::BlendOverride blendOverride = Animation::BlendOverride::Auto; - if (currentAnimation == Animation::Type::Fall || currentAnimation == Animation::Type::Jump || currentAnimation == Animation::Type::JumpEnd || currentAnimation == Animation::Type::JumpLandRun) - { - blendOverride = Animation::BlendOverride::None; - } + characterSingleton.positionOrRotationIsDirty = true; + } - if (moveForward) - { - // Run Forward - TryPlayAnimation(Animation::Bone::Default, Animation::Type::Run, Animation::Flag::Loop, blendOverride); - } - else if (moveBackward) - { - // Walk Backward + characterSingleton.positionOrRotationUpdateTimer += deltaTime; + } - TryPlayAnimation(Animation::Bone::Default, Animation::Type::Walkbackwards, Animation::Flag::Loop, blendOverride); - } + auto SetOrientation = [&](vec4& settings, f32 orientation) + { + f32 currentOrientation = settings.x; + if (orientation == currentOrientation) + return; - if (moveLeft || moveRight) - { - if (moveForward) - { - TryPlayAnimation(Animation::Bone::Default, Animation::Type::Run, Animation::Flag::Loop, blendOverride); - } - else if (moveBackward) - { - TryPlayAnimation(Animation::Bone::Default, Animation::Type::Walkbackwards, Animation::Flag::Loop, blendOverride); - } - else - { - TryPlayAnimation(Animation::Bone::Default, Animation::Type::Run, Animation::Flag::Loop, blendOverride); - } - } - } + settings.y = orientation; + settings.w = 0.0f; + settings.z = 1.0f / 8.0f; + }; + { + ::Util::Unit::UpdateAnimationState(registry, characterSingleton.modelEntity, model.instanceID, deltaTime); + if (isGrounded || (canControlInAir && isMoving)) { f32 spineOrientation = 0.0f; f32 headOrientation = 0.0f; f32 waistOrientation = 0.0f; - if (moveForward) + if (isMovingForward) { - if (moveRight) + if (isMovingRight) { spineOrientation = 30.0f; headOrientation = -15.0f; waistOrientation = 45.0f; } - else if (moveLeft) + else if (isMovingLeft) { spineOrientation = -30.0f; headOrientation = 15.0f; waistOrientation = -45.0f; } } - else if (moveBackward) + else if (isMovingBackward) { - if (moveRight) + if (isMovingRight) { spineOrientation = -30.0f; headOrientation = 15.0f; waistOrientation = -45.0f; } - else if (moveLeft) + else if (isMovingLeft) { spineOrientation = 30.0f; headOrientation = -15.0f; waistOrientation = 45.0f; } } - else if (moveRight) + else if (isMovingRight) { spineOrientation = 45.0f; headOrientation = -30.0f; waistOrientation = 90.0f; } - else if (moveLeft) + else if (isMovingLeft) { spineOrientation = -45.0f; headOrientation = 30.0f; @@ -481,65 +527,6 @@ namespace ECS::Systems } } - bool IsFalling = groundState == JPH::CharacterVirtual::EGroundState::OnSteepGround || groundState == JPH::CharacterVirtual::EGroundState::InAir; - if (isGrounded) - { - bool playJumpEnd = characterSingleton.jumpState == ECS::Singletons::JumpState::Fall || (characterSingleton.jumpState == ECS::Singletons::JumpState::End && currentAnimation != Animation::Type::JumpLandRun); - if (playJumpEnd) - { - bool canPlayJumpEnd = characterSingleton.jumpState == ECS::Singletons::JumpState::Fall; - if (canPlayJumpEnd) - { - if (!moveBackward && (moveForward || moveRight || moveLeft)) - { - if (TryPlayAnimation(Animation::Bone::Default, Animation::Type::JumpLandRun, Animation::Flag::Freeze, Animation::BlendOverride::None, OnJumpEndFinished)) - { - characterSingleton.jumpState = ECS::Singletons::JumpState::End; - } - } - else - { - if (moveBackward) - { - if (TryPlayAnimation(Animation::Bone::Default, Animation::Type::Walkbackwards, Animation::Flag::Loop, Animation::BlendOverride::Start)) - { - characterSingleton.jumpState = ECS::Singletons::JumpState::None; - } - } - else - { - if (TryPlayAnimation(Animation::Bone::Default, Animation::Type::JumpEnd, Animation::Flag::None, Animation::BlendOverride::None, OnJumpEndFinished)) - { - characterSingleton.jumpState = ECS::Singletons::JumpState::End; - } - } - } - } - } - else - { - if (!isPlayingJump && !moveForward && !moveBackward && !moveRight && !moveLeft) - { - TryPlayAnimation(Animation::Bone::Default, Animation::Type::Stand); - } - } - } - else if (IsFalling) - { - if (characterSingleton.jumpState == ECS::Singletons::JumpState::None || characterSingleton.jumpState == ECS::Singletons::JumpState::Fall) - { - Animation::BlendOverride blendOverride = Animation::BlendOverride::None; - - if (characterSingleton.jumpState == ECS::Singletons::JumpState::Fall) - blendOverride = Animation::BlendOverride::Start; - - TryPlayAnimation(Animation::Bone::Default, Animation::Type::Fall, Animation::Flag::Loop, blendOverride); - } - } - if (!isPlayingJump) - { - } - auto HandleUpdateOrientation = [](vec4& settings, f32 deltaTime) -> bool { if (settings.x == settings.y) @@ -554,20 +541,27 @@ namespace ECS::Systems return true; }; - if (HandleUpdateOrientation(characterSingleton.spineRotationSettings, deltaTime)) - { - quat rotation = glm::quat(glm::vec3(0.0f, glm::radians(characterSingleton.spineRotationSettings.x), 0.0f)); - animationSystem->SetBoneRotation(instanceID, Animation::Bone::SpineLow, rotation); - } - if (HandleUpdateOrientation(characterSingleton.headRotationSettings, deltaTime)) { - quat rotation = glm::quat(glm::vec3(0.0f, glm::radians(characterSingleton.headRotationSettings.x), 0.0f)); - animationSystem->SetBoneRotation(instanceID, Animation::Bone::Head, rotation); - } - if (HandleUpdateOrientation(characterSingleton.waistRotationSettings, deltaTime)) - { - quat rotation = glm::quat(glm::vec3(0.0f, glm::radians(characterSingleton.waistRotationSettings.x), 0.0f)); - animationSystem->SetBoneRotation(instanceID, Animation::Bone::Waist, rotation); + Animation::AnimationSystem* animationSystem = ServiceLocator::GetAnimationSystem(); + + if (model.modelID != std::numeric_limits().max() && model.instanceID != std::numeric_limits().max()) + { + if (HandleUpdateOrientation(characterSingleton.spineRotationSettings, deltaTime)) + { + quat rotation = glm::quat(glm::vec3(0.0f, glm::radians(characterSingleton.spineRotationSettings.x), 0.0f)); + animationSystem->SetBoneRotation(model.instanceID, Animation::Bone::SpineLow, rotation); + } + if (HandleUpdateOrientation(characterSingleton.headRotationSettings, deltaTime)) + { + quat rotation = glm::quat(glm::vec3(0.0f, glm::radians(characterSingleton.headRotationSettings.x), 0.0f)); + animationSystem->SetBoneRotation(model.instanceID, Animation::Bone::Head, rotation); + } + if (HandleUpdateOrientation(characterSingleton.waistRotationSettings, deltaTime)) + { + quat rotation = glm::quat(glm::vec3(0.0f, glm::radians(characterSingleton.waistRotationSettings.x), 0.0f)); + animationSystem->SetBoneRotation(model.instanceID, Animation::Bone::Waist, rotation); + } + } } } @@ -579,14 +573,15 @@ namespace ECS::Systems auto& transformSystem = ctx.get(); auto& activeCamera = ctx.get(); auto& orbitalCameraSettings = ctx.get(); - auto& characterSingleton = ctx.emplace(); + auto& characterSingleton = ctx.get(); + auto& movementInfo = registry.get(characterSingleton.modelEntity); ModelLoader* modelLoader = ServiceLocator::GetGameRenderer()->GetModelLoader(); u32 modelHash = modelLoader->GetModelHashFromModelPath("character/human/female/humanfemale.complexmodel"); - modelLoader->LoadModelForEntity(characterSingleton.modelEntity, modelHash); + modelLoader->LoadDisplayIDForEntity(characterSingleton.modelEntity, 50); - f32 width = 0.5f; - f32 height = 1.6f; + f32 width = 0.4166f; + f32 height = 1.9134f; f32 pyramidHeight = 0.25f; JPH::Array points = @@ -610,9 +605,12 @@ namespace ECS::Systems JPH::ConvexHullShapeSettings shapeSetting = JPH::ConvexHullShapeSettings(points, 0.0f); JPH::ShapeSettings::ShapeResult shapeResult = shapeSetting.Create(); + static constexpr f32 MaxWallClimbAngle = glm::radians(50.0f); + JPH::CharacterVirtualSettings characterSettings; characterSettings.mShape = shapeResult.Get(); characterSettings.mBackFaceMode = JPH::EBackFaceMode::IgnoreBackFaces; + characterSettings.mMaxSlopeAngle = MaxWallClimbAngle; if (characterSingleton.character) delete characterSingleton.character; @@ -628,13 +626,22 @@ namespace ECS::Systems newPosition = JPH::Vec3(0.0f, 0.0f, 0.0f); } + characterSingleton.targetEntity = entt::null; + characterSingleton.character->SetMass(1000000.0f); + characterSingleton.character->SetPenetrationRecoverySpeed(0.5f); characterSingleton.character->SetLinearVelocity(JPH::Vec3::sZero()); characterSingleton.character->SetPosition(newPosition); - characterSingleton.speed = 7.1111f; - characterSingleton.jumpState = ECS::Singletons::JumpState::None; - characterSingleton.pitch = 0.0f; - characterSingleton.yaw = 0.0f; + characterSingleton.positionOrRotationUpdateTimer = 0.0f; + characterSingleton.positionOrRotationIsDirty = true; + characterSingleton.canControlInAir = true; + + movementInfo.pitch = 0.0f; + movementInfo.yaw = 0.0f; + movementInfo.speed = 7.1111f; + movementInfo.jumpSpeed = 7.9555f; + movementInfo.gravityModifier = 1.0f; + movementInfo.jumpState = Components::JumpState::None; transformSystem.ParentEntityTo(characterSingleton.entity, orbitalCameraSettings.entity); transformSystem.SetLocalPosition(orbitalCameraSettings.entity, orbitalCameraSettings.cameraCurrentZoomOffset); diff --git a/Source/Game/Game/ECS/Systems/FreeflyingCamera.cpp b/Source/Game/Game/ECS/Systems/FreeflyingCamera.cpp index b93ec721..4848ecaa 100644 --- a/Source/Game/Game/ECS/Systems/FreeflyingCamera.cpp +++ b/Source/Game/Game/ECS/Systems/FreeflyingCamera.cpp @@ -24,8 +24,8 @@ namespace ECS::Systems void FreeflyingCamera::Init(entt::registry& registry) { entt::registry::context& ctx = registry.ctx(); - Singletons::ActiveCamera& activeCamera = ctx.emplace(); - Singletons::FreeflyingCameraSettings& settings = ctx.emplace(); + auto& activeCamera = ctx.emplace(); + auto& settings = ctx.emplace(); // Temporarily create a camera here for debugging { @@ -33,10 +33,10 @@ namespace ECS::Systems activeCamera.entity = cameraEntity; settings.entity = cameraEntity; - Components::Transform& transform = registry.emplace(cameraEntity); + auto& transform = registry.emplace(cameraEntity); TransformSystem::Get(registry).SetLocalPosition(cameraEntity, vec3(0, 10, -10)); - Components::Camera& camera = registry.emplace(cameraEntity); + auto& camera = registry.emplace(cameraEntity); camera.aspectRatio = static_cast(Renderer::Settings::SCREEN_WIDTH) / static_cast(Renderer::Settings::SCREEN_HEIGHT); camera.pitch = 30.0f; } @@ -95,16 +95,16 @@ namespace ECS::Systems { entt::registry::context& ctx = registry.ctx(); - Singletons::ActiveCamera& activeCamera = ctx.get(); - Singletons::FreeflyingCameraSettings& settings = ctx.get(); + auto& activeCamera = ctx.get(); + auto& settings = ctx.get(); if (activeCamera.entity != settings.entity) return; auto& tSystem = ECS::TransformSystem::Get(registry); - Components::Transform& cameraTransform = registry.get(activeCamera.entity); - Components::Camera& camera = registry.get(activeCamera.entity); + auto& cameraTransform = registry.get(activeCamera.entity); + auto& camera = registry.get(activeCamera.entity); vec3 cameraOffset = vec3(0.0f, 0.0f, 0.0f); // Input @@ -154,10 +154,10 @@ namespace ECS::Systems entt::registry::context& ctx = registry.ctx(); - Singletons::ActiveCamera& activeCamera = ctx.get(); - Singletons::FreeflyingCameraSettings& settings = ctx.get(); + auto& activeCamera = ctx.get(); + auto& settings = ctx.get(); - Components::Camera& camera = registry.get(activeCamera.entity); + auto& camera = registry.get(activeCamera.entity); if (settings.captureMouseHasMoved) { @@ -182,7 +182,7 @@ namespace ECS::Systems void FreeflyingCamera::CapturedMouseScrolled(entt::registry& registry, const vec2& position) { entt::registry::context& ctx = registry.ctx(); - Singletons::FreeflyingCameraSettings& settings = ctx.get(); + auto& settings = ctx.get(); f32 speed = settings.cameraSpeed; speed = speed + ((speed / 10.0f) * position.y); diff --git a/Source/Game/Game/ECS/Systems/NetworkConnection.cpp b/Source/Game/Game/ECS/Systems/NetworkConnection.cpp index 60fc02aa..48906c35 100644 --- a/Source/Game/Game/ECS/Systems/NetworkConnection.cpp +++ b/Source/Game/Game/ECS/Systems/NetworkConnection.cpp @@ -1,6 +1,19 @@ #include "NetworkConnection.h" +#include "Game/Animation/AnimationSystem.h" +#include "Game/ECS/Components/AABB.h" +#include "Game/ECS/Components/CastInfo.h" +#include "Game/ECS/Components/DisplayInfo.h" +#include "Game/ECS/Components/Model.h" +#include "Game/ECS/Components/MovementInfo.h" +#include "Game/ECS/Components/Name.h" +#include "Game/ECS/Components/NetworkedEntity.h" +#include "Game/ECS/Components/UnitStatsComponent.h" +#include "Game/ECS/Singletons/CharacterSingleton.h" #include "Game/ECS/Singletons/NetworkState.h" +#include "Game/ECS/Util/Transforms.h" +#include "Game/Rendering/GameRenderer.h" +#include "Game/Rendering/Model/ModelLoader.h" #include "Game/Util/ServiceLocator.h" #include @@ -8,76 +21,631 @@ #include #include -#include #include #include - -AutoCVar_Int CVAR_DebugAutoConnectToGameserver(CVarCategory::Client | CVarCategory::Network, "autoConnectToGameserver", "Automatically connect to game server when launching client", 0, CVarFlags::EditCheckbox); +#include namespace ECS::Systems { - bool HandleOnConnected(Network::SocketID socketID, std::shared_ptr netPacket) + bool HandleOnConnected(Network::SocketID socketID, std::shared_ptr& recvPacket) { u8 result = 0; - if (!netPacket->payload->GetU8(result)) + if (!recvPacket->GetU8(result)) return false; - if (result == 0) + if (result != 0) { - std::string charName = ""; - if (!netPacket->payload->GetString(charName)) + std::string errorText = ""; + + if (!recvPacket->GetString(errorText)) return false; - NC_LOG_INFO("Network : Connected to server (Playing on character : \"{0}\")", charName); + NC_LOG_WARNING("Network : Failed to connect to server ({0})", errorText); + return false; } + entt::entity networkID = entt::null; + std::string charName = ""; + + if (!recvPacket->Get(networkID)) + return false; + + if (!recvPacket->GetString(charName)) + return false; + + entt::registry* registry = ServiceLocator::GetEnttRegistries()->gameRegistry; + Singletons::CharacterSingleton& characterSingleton = registry->ctx().get(); + Singletons::NetworkState& networkState = registry->ctx().get(); + + networkState.networkIDToEntity[networkID] = characterSingleton.modelEntity; + networkState.entityToNetworkID[characterSingleton.modelEntity] = networkID; + + NC_LOG_INFO("Network : Connected to server (Playing on character : \"{0}\")", charName); + return true; } - void NetworkConnection::Init(entt::registry& registry) + bool HandleOnResourceUpdate(Network::SocketID socketID, std::shared_ptr& recvPacket) { - entt::registry::context& ctx = registry.ctx(); + entt::entity networkID = entt::null; + Components::PowerType powerType = Components::PowerType::Count; + f32 powerBaseValue = 0.0f; + f32 powerCurrentValue = 0.0f; + f32 powerMaxValue = 0.0f; - Singletons::NetworkState& networkState = ctx.emplace(); + if (!recvPacket->Get(networkID)) + return false; - // Setup NetworkState + if (!recvPacket->Get(powerType)) + return false; + + if (!recvPacket->GetF32(powerBaseValue)) + return false; + + if (!recvPacket->GetF32(powerCurrentValue)) + return false; + + if (!recvPacket->GetF32(powerMaxValue)) + return false; + + if (powerType >= Components::PowerType::Count) { - networkState.client = std::make_unique(); + NC_LOG_WARNING("Network : Received Power Update for unknown PowerType ({0})", static_cast(powerType)); + return true; + } - networkState.packetHandler = std::make_unique(); - networkState.packetHandler->SetMessageHandler(Network::Opcode::SMSG_CONNECTED, Network::OpcodeHandler(Network::ConnectionStatus::AUTH_NONE, 4u, 12u, &HandleOnConnected)); + entt::registry* registry = ServiceLocator::GetEnttRegistries()->gameRegistry; + auto& networkState = registry->ctx().get(); + + if (!networkState.networkIDToEntity.contains(networkID)) + { + NC_LOG_WARNING("Network : Received Power Update for non existing entity ({0})", entt::to_integral(networkID)); + return true; + } + + entt::entity entity = networkState.networkIDToEntity[networkID]; + + if (!registry->valid(entity)) + { + NC_LOG_WARNING("Network : Received Power Update for non existing entity ({0})", entt::to_integral(networkID)); + return true; + } + + auto& unitStatsComponent = registry->get(entity); + + if (powerType == Components::PowerType::Health) + { + unitStatsComponent.baseHealth = powerBaseValue; + unitStatsComponent.currentHealth = powerCurrentValue; + unitStatsComponent.maxHealth = powerMaxValue; + } + else + { + unitStatsComponent.basePower[(u32)powerType] = powerBaseValue; + unitStatsComponent.currentPower[(u32)powerType] = powerCurrentValue; + unitStatsComponent.maxPower[(u32)powerType] = powerMaxValue; + } + + return true; + } + + bool HandleOnEntityCreate(Network::SocketID socketID, std::shared_ptr& recvPacket) + { + entt::entity networkID = entt::null; + u32 displayID = 0; + vec3 position = vec3(0.0f); + quat rotation = quat(1.0f, 0.0f, 0.0f, 0.0f); + vec3 scale = vec3(1.0f); + + if (!recvPacket->Get(networkID)) + return false; + + if (!recvPacket->Get(displayID)) + return false; + + if (!recvPacket->Get(position)) + return false; + + if (!recvPacket->Get(rotation)) + return false; + + if (!recvPacket->Get(scale)) + return false; + + entt::registry* registry = ServiceLocator::GetEnttRegistries()->gameRegistry; + Singletons::NetworkState& networkState = registry->ctx().get(); + + if (networkState.networkIDToEntity.contains(networkID)) + { + NC_LOG_WARNING("Network : Received Create Entity for already existing entity ({0})", entt::to_integral(networkID)); + return true; + } + + entt::entity newEntity = registry->create(); + registry->emplace(newEntity); + registry->emplace(newEntity); + registry->emplace(newEntity); + registry->emplace(newEntity); + registry->emplace(newEntity); + registry->emplace(newEntity); + auto& displayInfo = registry->emplace(newEntity); + displayInfo.displayID = displayID; + + auto& networkedEntity = registry->emplace(newEntity); + networkedEntity.networkID = networkID; + + ModelLoader* modelLoader = ServiceLocator::GetGameRenderer()->GetModelLoader(); + modelLoader->LoadDisplayIDForEntity(newEntity, displayID); + + TransformSystem& transformSystem = TransformSystem::Get(*registry); + transformSystem.SetWorldPosition(newEntity, position); + transformSystem.SetWorldRotation(newEntity, rotation); + transformSystem.SetLocalScale(newEntity, scale); + + networkState.networkIDToEntity[networkID] = newEntity; + networkState.entityToNetworkID[newEntity] = networkID; + + return true; + } + + bool HandleOnEntityDestroy(Network::SocketID socketID, std::shared_ptr& recvPacket) + { + entt::entity networkID = entt::null; + + if (!recvPacket->Get(networkID)) + return false; + + entt::registry* registry = ServiceLocator::GetEnttRegistries()->gameRegistry; + Singletons::NetworkState& networkState = registry->ctx().get(); + + if (!networkState.networkIDToEntity.contains(networkID)) + { + NC_LOG_WARNING("Network : Received Delete Entity for unknown entity ({0})", entt::to_integral(networkID)); + return true; + } + + entt::entity entity = networkState.networkIDToEntity[networkID]; + + if (registry->any_of(entity)) + { + Animation::AnimationSystem* AnimationSystem = ServiceLocator::GetAnimationSystem(); + ModelLoader* modelLoader = ServiceLocator::GetGameRenderer()->GetModelLoader(); + + auto& model = registry->get(entity); + AnimationSystem->RemoveInstance(model.instanceID); + modelLoader->UnloadModelForEntity(entity, model.instanceID); + } + + networkState.networkIDToEntity.erase(networkID); + networkState.entityToNetworkID.erase(entity); + + registry->destroy(entity); + + return true; + } + + bool HandleOnEntityMove(Network::SocketID socketID, std::shared_ptr& recvPacket) + { + entt::entity networkID = entt::null; + vec3 position = vec3(0.0f); + quat rotation = quat(1.0f, 0.0f, 0.0f, 0.0f); + Components::MovementFlags movementFlags = {}; + f32 verticalVelocity = 0.0f; + + if (!recvPacket->Get(networkID)) + return false; + + if (!recvPacket->Get(position)) + return false; + + if (!recvPacket->Get(rotation)) + return false; + + if (!recvPacket->Get(movementFlags)) + return false; + + if (!recvPacket->Get(verticalVelocity)) + return false; + + entt::registry* registry = ServiceLocator::GetEnttRegistries()->gameRegistry; + Singletons::NetworkState& networkState = registry->ctx().get(); + + if (!networkState.networkIDToEntity.contains(networkID)) + { + NC_LOG_WARNING("Network : Received Entity Move for non existing entity ({0})", entt::to_integral(networkID)); + return true; + } + + entt::entity entity = networkState.networkIDToEntity[networkID]; + + auto& networkedEntity = registry->get(entity); + auto& transform = registry->get(entity); + auto& movementInfo = registry->get(entity); + + networkedEntity.initialPosition = transform.GetWorldPosition(); + networkedEntity.desiredPosition = position; + networkedEntity.positionProgress = 0.0f; + networkedEntity.positionOrRotationChanged = true; + movementInfo.movementFlags = movementFlags; + movementInfo.verticalVelocity = verticalVelocity; + + TransformSystem& transformSystem = TransformSystem::Get(*registry); + transformSystem.SetWorldRotation(entity, rotation); + + return true; + } + + bool HandleOnEntityTargetUpdate(Network::SocketID socketID, std::shared_ptr& recvPacket) + { + entt::entity networkID = entt::null; + entt::entity targetNetworkID = entt::null; + + if (!recvPacket->Get(networkID)) + return false; + + if (!recvPacket->Get(targetNetworkID)) + return false; + + entt::registry* registry = ServiceLocator::GetEnttRegistries()->gameRegistry; + Singletons::NetworkState& networkState = registry->ctx().get(); + + if (!networkState.networkIDToEntity.contains(networkID)) + { + NC_LOG_WARNING("Network : Received Target Update for non existing entity ({0})", entt::to_integral(networkID)); + return true; + } + + if (!networkState.networkIDToEntity.contains(targetNetworkID)) + { + NC_LOG_WARNING("Network : Received Target Update for non existing target entity ({0})", entt::to_integral(targetNetworkID)); + return true; + } + + Singletons::CharacterSingleton& characterSingleton = registry->ctx().get(); + + entt::entity entity = networkState.networkIDToEntity[networkID]; + entt::entity targetEntity = networkState.networkIDToEntity[targetNetworkID]; + + if (entity == characterSingleton.entity) + { + characterSingleton.targetEntity = targetEntity; + } + else + { + auto& networkedEntity = registry->get(entity); + networkedEntity.targetEntity = targetEntity; + } + + return true; + } + + bool HandleOnSpellCastResult(Network::SocketID socketID, std::shared_ptr& recvPacket) + { + u8 result; + + if (!recvPacket->GetU8(result)) + return false; + + if (result != 0) + { + std::string errorText = ""; + + if (!recvPacket->GetString(errorText)) + return false; + + NC_LOG_WARNING("Network : Spell Cast Failed - {0}", errorText); + } + else + { + f32 castTime = 0.0f; + f32 duration = 0.0f; + + if (!recvPacket->GetF32(castTime)) + return false; - Network::Socket::Result initResult = networkState.client->Init(Network::Socket::Mode::TCP); - if (initResult != Network::Socket::Result::SUCCESS) + if (!recvPacket->GetF32(duration)) + return false; + + entt::registry* registry = ServiceLocator::GetEnttRegistries()->gameRegistry; + Singletons::CharacterSingleton& characterSingleton = registry->ctx().get(); + + Components::CastInfo& castInfo = registry->emplace_or_replace(characterSingleton.modelEntity); + castInfo.target = characterSingleton.targetEntity; + castInfo.castTime = castTime; + castInfo.duration = duration; + } + + return true; + } + bool HandleOnEntityCastSpell(Network::SocketID socketID, std::shared_ptr& recvPacket) + { + entt::entity networkID = entt::null; + f32 castTime = 0.0f; + f32 duration = 0.0f; + + if (!recvPacket->Get(networkID)) + return false; + + if (!recvPacket->GetF32(castTime)) + return false; + + if (!recvPacket->GetF32(duration)) + return false; + + entt::registry* registry = ServiceLocator::GetEnttRegistries()->gameRegistry; + Singletons::NetworkState& networkState = registry->ctx().get(); + + if (!networkState.networkIDToEntity.contains(networkID)) + { + NC_LOG_WARNING("Network : Received Cast Spell for non existing entity ({0})", entt::to_integral(networkID)); + return true; + } + + entt::entity entity = networkState.networkIDToEntity[networkID]; + + if (!registry->valid(entity)) + { + NC_LOG_WARNING("Network : Received Cast Spell for non existing entity ({0})", entt::to_integral(networkID)); + return true; + } + + Components::NetworkedEntity* networkedEntity = registry->try_get(entity); + if (!networkedEntity) + { + NC_LOG_WARNING("Network : Received Cast Spell for non existing entity ({0})", entt::to_integral(networkID)); + return true; + } + + Components::CastInfo& castInfo = registry->emplace_or_replace(entity); + + castInfo.target = networkedEntity->targetEntity; + castInfo.castTime = castTime; + castInfo.duration = duration; + + return true; + } + bool HandleOnCombatEvent(Network::SocketID socketID, std::shared_ptr& recvPacket) + { + u16 eventID = 0; + entt::entity sourceNetworkID = entt::null; + + if (!recvPacket->GetU16(eventID)) + return false; + + if (!recvPacket->Get(sourceNetworkID)) + return false; + + entt::registry* registry = ServiceLocator::GetEnttRegistries()->gameRegistry; + Singletons::NetworkState& networkState = registry->ctx().get(); + + if (!networkState.networkIDToEntity.contains(sourceNetworkID)) + { + NC_LOG_WARNING("Network : Received Combat Event for non existing entity ({0})", entt::to_integral(sourceNetworkID)); + return true; + } + + entt::entity sourceEntity = networkState.networkIDToEntity[sourceNetworkID]; + + if (!registry->valid(sourceEntity)) + { + NC_LOG_WARNING("Network : Received Combat Event for non existing entity ({0})", entt::to_integral(sourceNetworkID)); + return true; + } + + switch (eventID) + { + // Damage Taken + case 0: + case 1: { - NC_LOG_ERROR("Network : Failed to initialize Client"); - return; - } + entt::entity targetNetworkID = entt::null; + f32 value = 0.0f; + + if (!recvPacket->Get(targetNetworkID)) + return false; + + if (!recvPacket->GetF32(value)) + return false; + + if (!networkState.networkIDToEntity.contains(targetNetworkID)) + { + NC_LOG_WARNING("Network : Received Combat Event for non existing target entity ({0})", entt::to_integral(targetNetworkID)); + return true; + } + + entt::entity targetEntity = networkState.networkIDToEntity[targetNetworkID]; + + if (!registry->valid(targetEntity)) + { + NC_LOG_WARNING("Network : Received Combat Event for non existing target entity ({0})", entt::to_integral(targetNetworkID)); + return true; + } - networkState.client->GetSocket()->SetBlockingState(false); + Components::Name* sourceName = registry->try_get(sourceEntity); + Components::Name* targetName = registry->try_get(targetEntity); - if (CVAR_DebugAutoConnectToGameserver.Get() == 1) + if (!sourceName || !targetName) + { + NC_LOG_WARNING("Network : Received Combat Event for entity without Name Component ({0})", entt::to_integral(targetNetworkID)); + return true; + } + + if (eventID == 0) + { + // Damage Dealt + ImGui::InsertNotification({ ImGuiToastType::Success, 3000, "%s dealt %.2f damage to %s", sourceName->name.c_str(), value, targetName->name.c_str() }); + } + else + { + // Healing Taken + ImGui::InsertNotification({ ImGuiToastType::Success, 3000, "%s healed %s for %.2f", sourceName->name.c_str(), value, targetName->name.c_str() }); + } + + break; + } + + case 2: { - // Connect to IP/Port - std::string ipAddress = "127.0.0.1"; - u16 port = 4000; + entt::entity targetNetworkID = entt::null; + + if (!recvPacket->Get(targetNetworkID)) + return false; + + if (!networkState.networkIDToEntity.contains(targetNetworkID)) + { + NC_LOG_WARNING("Network : Received Combat Event for non existing target entity ({0})", entt::to_integral(targetNetworkID)); + return true; + } + + entt::entity targetEntity = networkState.networkIDToEntity[targetNetworkID]; + + if (!registry->valid(targetEntity)) + { + NC_LOG_WARNING("Network : Received Combat Event for non existing target entity ({0})", entt::to_integral(targetNetworkID)); + return true; + } - Network::Socket::Result connectResult = networkState.client->Connect(ipAddress, port); - if (connectResult != Network::Socket::Result::SUCCESS && - connectResult != Network::Socket::Result::ERROR_WOULD_BLOCK) + Components::Name* sourceName = registry->try_get(sourceEntity); + Components::Name* targetName = registry->try_get(targetEntity); + + if (!sourceName || !targetName) { - NC_LOG_ERROR("Network : Failed to connect to ({0}, {1})", ipAddress, port); + NC_LOG_WARNING("Network : Received Combat Event for entity without Name Component ({0})", entt::to_integral(targetNetworkID)); + return true; } + + ImGui::InsertNotification({ ImGuiToastType::Success, 3000, "%s ressurected %s", sourceName->name.c_str(), targetName->name.c_str() }); + break; + } + + default: + { + break; } } + return true; + } + + bool HandleOnEntityDisplayInfoUpdate(Network::SocketID socketID, std::shared_ptr& recvPacket) + { + entt::entity networkID = entt::null; + u32 displayID = 0; + + if (!recvPacket->Get(networkID)) + return false; + + if (!recvPacket->GetU32(displayID)) + return false; + + entt::registry* registry = ServiceLocator::GetEnttRegistries()->gameRegistry; + Singletons::NetworkState& networkState = registry->ctx().get(); + + if (!networkState.networkIDToEntity.contains(networkID)) + { + NC_LOG_WARNING("Network : Received Display Info Update for non existing entity ({0})", entt::to_integral(networkID)); + return true; + } + + entt::entity entity = networkState.networkIDToEntity[networkID]; + if (!registry->valid(entity)) + { + NC_LOG_WARNING("Network : Received Display Info Update for non existing entity ({0})", entt::to_integral(networkID)); + return true; + } + + ModelLoader* modelLoader = ServiceLocator::GetGameRenderer()->GetModelLoader(); + if (!modelLoader->LoadDisplayIDForEntity(entity, displayID)) + { + NC_LOG_WARNING("Network : Failed to load DisplayID for entity ({0})", entt::to_integral(networkID)); + return true; + } + + auto& displayInfo = registry->get(entity); + displayInfo.displayID = displayID; + + return true; + } + + bool HandleOnCheatCreateCharacterResult(Network::SocketID socketID, std::shared_ptr& recvPacket) + { + u8 result = 0; + std::string resultText = ""; + + if (!recvPacket->GetU8(result)) + return false; + + if (!recvPacket->GetString(resultText)) + return false; + + if (result != 0) + { + NC_LOG_WARNING("Network : ({0})", resultText); + return true; + } + else + { + NC_LOG_INFO("Network : ({0})", resultText); + return true; + } + } + + bool HandleOnCheatDeleteCharacterResult(Network::SocketID socketID, std::shared_ptr& recvPacket) + { + u8 result = 0; + std::string resultText = ""; + + if (!recvPacket->GetU8(result)) + return false; + + if (!recvPacket->GetString(resultText)) + return false; + + if (result != 0) + { + NC_LOG_WARNING("Network : ({0})", resultText); + return true; + } + else + { + NC_LOG_INFO("Network : ({0})", resultText); + return true; + } + } + + void NetworkConnection::Init(entt::registry& registry) + { + entt::registry::context& ctx = registry.ctx(); + + auto& networkState = ctx.emplace(); + + // Setup NetworkState + { + networkState.client = std::make_unique(); + networkState.networkIDToEntity.reserve(1024); + networkState.entityToNetworkID.reserve(1024); + + networkState.packetHandler = std::make_unique(); + networkState.packetHandler->SetMessageHandler(Network::Opcode::SMSG_CONNECTED, Network::OpcodeHandler(Network::ConnectionStatus::AUTH_NONE, 8u, 64u, &HandleOnConnected)); + networkState.packetHandler->SetMessageHandler(Network::Opcode::SMSG_ENTITY_RESOURCES_UPDATE, Network::OpcodeHandler(Network::ConnectionStatus::AUTH_NONE, 20u, 20u, &HandleOnResourceUpdate)); + networkState.packetHandler->SetMessageHandler(Network::Opcode::SMSG_ENTITY_CREATE, Network::OpcodeHandler(Network::ConnectionStatus::AUTH_NONE, 48u, 48u, &HandleOnEntityCreate)); + networkState.packetHandler->SetMessageHandler(Network::Opcode::SMSG_ENTITY_DESTROY, Network::OpcodeHandler(Network::ConnectionStatus::AUTH_NONE, 4u, 4u, &HandleOnEntityDestroy)); + networkState.packetHandler->SetMessageHandler(Network::Opcode::MSG_ENTITY_MOVE, Network::OpcodeHandler(Network::ConnectionStatus::AUTH_NONE, 40u, 40u, &HandleOnEntityMove)); + networkState.packetHandler->SetMessageHandler(Network::Opcode::MSG_ENTITY_TARGET_UPDATE, Network::OpcodeHandler(Network::ConnectionStatus::AUTH_NONE, 8u, 8u, &HandleOnEntityTargetUpdate)); + networkState.packetHandler->SetMessageHandler(Network::Opcode::SMSG_SEND_SPELLCAST_RESULT, Network::OpcodeHandler(Network::ConnectionStatus::AUTH_NONE, 9u, 65u, &HandleOnSpellCastResult)); + networkState.packetHandler->SetMessageHandler(Network::Opcode::SMSG_ENTITY_CAST_SPELL, Network::OpcodeHandler(Network::ConnectionStatus::AUTH_NONE, 12u, 12u, &HandleOnEntityCastSpell)); + networkState.packetHandler->SetMessageHandler(Network::Opcode::SMSG_COMBAT_EVENT, Network::OpcodeHandler(Network::ConnectionStatus::AUTH_NONE, 6u, 22u, &HandleOnCombatEvent)); + networkState.packetHandler->SetMessageHandler(Network::Opcode::SMSG_ENTITY_DISPLAYINFO_UPDATE, Network::OpcodeHandler(Network::ConnectionStatus::AUTH_NONE, 8u, 8u, &HandleOnEntityDisplayInfoUpdate)); + networkState.packetHandler->SetMessageHandler(Network::Opcode::SMSG_CHEAT_CREATE_CHARACTER_RESULT, Network::OpcodeHandler(Network::ConnectionStatus::AUTH_NONE, 2u, 53u, &HandleOnCheatCreateCharacterResult)); + networkState.packetHandler->SetMessageHandler(Network::Opcode::SMSG_CHEAT_DELETE_CHARACTER_RESULT, Network::OpcodeHandler(Network::ConnectionStatus::AUTH_NONE, 2u, 55u, &HandleOnCheatDeleteCharacterResult)); + } } void NetworkConnection::Update(entt::registry& registry, f32 deltaTime) { entt::registry::context& ctx = registry.ctx(); - Singletons::NetworkState& networkState = ctx.get(); + auto& networkState = ctx.get(); static bool wasConnected = false; if (networkState.client->IsConnected()) @@ -106,68 +674,68 @@ namespace ECS::Systems while (size_t activeSize = buffer->GetActiveSize()) { // We have received a partial header and need to read more - if (activeSize < sizeof(Network::Packet::Header)) + if (activeSize < sizeof(Network::PacketHeader)) { buffer->Normalize(); break; } - Network::Packet::Header* header = reinterpret_cast(buffer->GetReadPointer()); + Network::PacketHeader* header = reinterpret_cast(buffer->GetReadPointer()); if (header->opcode == Network::Opcode::INVALID || header->opcode > Network::Opcode::MAX_COUNT) { -#ifdef NC_Debug +#ifdef NC_DEBUG NC_LOG_ERROR("Network : Received Invalid Opcode ({0}) from server", static_cast::type>(header->opcode)); -#endif // NC_Debug +#endif // NC_DEBUG networkState.client->Close(); break; } if (header->size > Network::DEFAULT_BUFFER_SIZE) { -#ifdef NC_Debug +#ifdef NC_DEBUG NC_LOG_ERROR("Network : Received Invalid Opcode Size ({0} : {1}) from server", static_cast::type>(header->opcode), header->size); -#endif // NC_Debug +#endif // NC_DEBUG networkState.client->Close(); break; } - size_t receivedPayloadSize = activeSize - sizeof(Network::Packet::Header); + size_t receivedPayloadSize = activeSize - sizeof(Network::PacketHeader); if (receivedPayloadSize < header->size) { buffer->Normalize(); break; } - buffer->SkipRead(sizeof(Network::Packet::Header)); + buffer->SkipRead(sizeof(Network::PacketHeader)); - std::shared_ptr packet = Network::Packet::Borrow(); + std::shared_ptr messageBuffer = Bytebuffer::Borrow(); { - // Header - { - packet->header = *header; - } - // Payload { - if (packet->header.size) + messageBuffer->Put(*header); + + if (header->size) { - packet->payload = Bytebuffer::Borrow(); - packet->payload->size = packet->header.size; - packet->payload->writtenData = packet->header.size; - std::memcpy(packet->payload->GetDataPointer(), buffer->GetReadPointer(), packet->header.size); + std::memcpy(messageBuffer->GetWritePointer(), buffer->GetReadPointer(), header->size); + messageBuffer->SkipWrite(header->size); // Skip Payload buffer->SkipRead(header->size); } } - if (!networkState.packetHandler->CallHandler(Network::SOCKET_ID_INVALID, packet)) + if (!networkState.packetHandler->CallHandler(Network::SOCKET_ID_INVALID, messageBuffer)) { networkState.client->Close(); } } } + + if (buffer->GetActiveSize() == 0) + { + buffer->Reset(); + } } } } \ No newline at end of file diff --git a/Source/Game/Game/ECS/Systems/OrbitalCamera.cpp b/Source/Game/Game/ECS/Systems/OrbitalCamera.cpp index f41b5176..4045a0ca 100644 --- a/Source/Game/Game/ECS/Systems/OrbitalCamera.cpp +++ b/Source/Game/Game/ECS/Systems/OrbitalCamera.cpp @@ -1,5 +1,6 @@ #include "OrbitalCamera.h" +#include "Game/ECS/Components/MovementInfo.h" #include "Game/ECS/Singletons/CharacterSingleton.h" #include "Game/ECS/Singletons/OrbitalCameraSettings.h" #include "Game/ECS/Singletons/ActiveCamera.h" @@ -33,8 +34,8 @@ namespace ECS::Systems void OrbitalCamera::Init(entt::registry& registry) { entt::registry::context& ctx = registry.ctx(); - Singletons::ActiveCamera& activeCamera = ctx.emplace(); - Singletons::OrbitalCameraSettings& settings = ctx.emplace(); + auto& activeCamera = ctx.emplace(); + auto& settings = ctx.emplace(); // Temporarily create a camera here for debugging { @@ -162,8 +163,8 @@ namespace ECS::Systems { entt::registry::context& ctx = registry.ctx(); - Singletons::ActiveCamera& activeCamera = ctx.get(); - Singletons::OrbitalCameraSettings& settings = ctx.get(); + auto& activeCamera = ctx.get(); + auto& settings = ctx.get(); auto& characterSingleton = ctx.get(); if (activeCamera.entity != settings.entity) @@ -171,15 +172,15 @@ namespace ECS::Systems auto& tSystem = ECS::TransformSystem::Get(registry); - Components::Transform& cameraTransform = registry.get(activeCamera.entity); - Components::Camera& camera = registry.get(activeCamera.entity); + auto& cameraTransform = registry.get(activeCamera.entity); + auto& camera = registry.get(activeCamera.entity); settings.cameraZoomProgress += settings.cameraZoomSpeed * deltaTime; settings.cameraZoomProgress = glm::clamp(settings.cameraZoomProgress, 0.0f, 1.0f); settings.cameraCurrentZoomOffset = glm::mix(settings.cameraCurrentZoomOffset, settings.cameraTargetZoomOffset, settings.cameraZoomProgress); - Components::Transform& characterControllerTransform = registry.get(characterSingleton.entity); - Components::Transform& characterModelTransform = registry.get(characterSingleton.modelEntity); + auto& characterControllerTransform = registry.get(characterSingleton.entity); + auto& characterModelTransform = registry.get(characterSingleton.modelEntity); const mat4x4& characterControllerMatrix = characterControllerTransform.GetMatrix(); // This is the point we want to rotate around vec3 eulerAngles = vec3(glm::radians(camera.pitch), glm::radians(camera.yaw), glm::radians(camera.roll)); @@ -196,12 +197,14 @@ namespace ECS::Systems if (settings.mouseRightDown) { - Singletons::CharacterSingleton& characterSingleton = ctx.get(); + auto& characterSingleton = ctx.get(); + auto& movementInfo = registry.get(characterSingleton.modelEntity); glm::mat3 rotationMatrix = glm::mat3_cast(resultRotation); f32 yaw = glm::radians(camera.yaw); - characterSingleton.yaw = yaw + glm::pi(); + movementInfo.yaw = yaw + glm::pi(); + characterSingleton.positionOrRotationIsDirty = true; } tSystem.SetWorldRotation(activeCamera.entity, resultRotation); @@ -217,10 +220,10 @@ namespace ECS::Systems entt::registry::context& ctx = registry.ctx(); - Singletons::ActiveCamera& activeCamera = ctx.get(); - Singletons::OrbitalCameraSettings& settings = ctx.get(); + auto& activeCamera = ctx.get(); + auto& settings = ctx.get(); - Components::Camera& camera = registry.get(activeCamera.entity); + auto& camera = registry.get(activeCamera.entity); if (settings.captureMouseHasMoved) { @@ -245,11 +248,12 @@ namespace ECS::Systems void OrbitalCamera::CapturedMouseScrolled(entt::registry& registry, const vec2& position) { entt::registry::context& ctx = registry.ctx(); - Singletons::CharacterSingleton& characterSingleton = ctx.get(); + auto& characterSingleton = ctx.get(); + auto& movementInfo = registry.get(characterSingleton.modelEntity); - f32 speed = characterSingleton.speed; + f32 speed = movementInfo.speed; speed = speed + ((speed / 20.0f) * position.y); speed = glm::max(speed, 7.1111f); - characterSingleton.speed = speed; + movementInfo.speed = speed; } } \ No newline at end of file diff --git a/Source/Game/Game/ECS/Systems/UpdateNetworkedEntity.cpp b/Source/Game/Game/ECS/Systems/UpdateNetworkedEntity.cpp new file mode 100644 index 00000000..41045dd5 --- /dev/null +++ b/Source/Game/Game/ECS/Systems/UpdateNetworkedEntity.cpp @@ -0,0 +1,231 @@ +#include "UpdateNetworkedEntity.h" + +#include "Game/Animation/AnimationSystem.h" +#include "Game/ECS/Components/Model.h" +#include "Game/ECS/Components/MovementInfo.h" +#include "Game/ECS/Components/NetworkedEntity.h" +#include "Game/ECS/Components/UnitStatsComponent.h" +#include "Game/ECS/Singletons/JoltState.h" +#include "Game/ECS/Util/Transforms.h" +#include "Game/Util/ServiceLocator.h" +#include "Game/Util/UnitUtil.h" + +#include +#include +#include +#include + +namespace ECS::Systems +{ + class NetworkedEntityFilter : public JPH::BodyFilter + { + public: + NetworkedEntityFilter(u32 bodyID) : _bodyID(bodyID) { } + + bool ShouldCollide(const JPH::BodyID& inBodyID) const override + { + return _bodyID != inBodyID; + } + + bool ShouldCollideLocked(const JPH::Body& inBody) const override + { + return _bodyID != inBody.GetID(); + } + + private: + JPH::BodyID _bodyID; + }; + + void UpdateNetworkedEntity::Init(entt::registry& registry) + { + } + + void UpdateNetworkedEntity::Update(entt::registry& registry, f32 deltaTime) + { + auto view = registry.view(); + + view.each([&](entt::entity entity, Components::Transform& transform, Components::MovementInfo& movementInfo, Components::NetworkedEntity& networkedEntity) + { + if (networkedEntity.positionProgress != -1.0f) + { + networkedEntity.positionProgress += 10.0f * deltaTime; + + vec3 initialPosition = networkedEntity.initialPosition; + vec3 desiredPosition = networkedEntity.desiredPosition; + f32 progress = glm::clamp(networkedEntity.positionProgress, 0.0f, 1.0f); + vec3 newPosition = glm::mix(initialPosition, desiredPosition, progress); + + if (networkedEntity.bodyID != std::numeric_limits().max()) + { + Singletons::JoltState& joltState = registry.ctx().get(); + + JPH::BodyID bodyID = JPH::BodyID(networkedEntity.bodyID); + quat rotation = transform.GetWorldRotation(); + + JPH::Vec3 start = JPH::Vec3(newPosition.x, newPosition.y, newPosition.z); + JPH::Vec3 direction = JPH::Vec3(0.0f, -2.0f, 0.0f); + + JPH::RRayCast ray(start, direction); + JPH::RayCastResult hit; + + + bool isGrounded = movementInfo.movementFlags.grounded; + if (isGrounded) + { + if (movementInfo.movementFlags.jumping || movementInfo.jumpState != Components::JumpState::None) + { + movementInfo.jumpState = Components::JumpState::None; + } + } + else + { + bool isInJump = movementInfo.movementFlags.jumping; + if (isInJump) + { + if (movementInfo.jumpState == Components::JumpState::None) + movementInfo.jumpState = Components::JumpState::Begin; + } + } + + joltState.physicsSystem.GetBodyInterface().SetPositionAndRotation(bodyID, JPH::Vec3(newPosition.x, newPosition.y, newPosition.z), JPH::Quat(rotation.x, rotation.y, rotation.z, rotation.w), JPH::EActivation::DontActivate); + } + + TransformSystem& transformSystem = TransformSystem::Get(registry); + transformSystem.SetWorldPosition(entity, newPosition); + + networkedEntity.positionOrRotationChanged = true; + + if (networkedEntity.positionProgress >= 1.0f) + { + networkedEntity.positionProgress = -1.0f; + } + } + + Animation::AnimationSystem* animationSystem = ServiceLocator::GetAnimationSystem(); + if (auto* model = registry.try_get(entity)) + { + u32 instanceID = model->instanceID; + auto& unitStatsComponent = registry.get(entity); + + auto SetOrientation = [&](vec4& settings, f32 orientation) + { + f32 currentOrientation = settings.x; + if (orientation == currentOrientation) + return; + + settings.y = orientation; + settings.w = 0.0f; + + f32 timeToChange = glm::abs(orientation - currentOrientation) / 450.0f; + timeToChange = glm::max(timeToChange, 0.01f); + settings.z = timeToChange; + }; + + if (networkedEntity.bodyID != std::numeric_limits().max()) + { + Singletons::JoltState& joltState = registry.ctx().get(); + + JPH::BodyID bodyID = JPH::BodyID(networkedEntity.bodyID); + + + } + + ::Util::Unit::UpdateAnimationState(registry, entity, instanceID, deltaTime); + + bool isAlive = unitStatsComponent.currentHealth > 0.0f; + if (isAlive) + { + bool isMovingForward = movementInfo.movementFlags.forward; + bool isMovingBackward = movementInfo.movementFlags.backward; + bool isMovingLeft = movementInfo.movementFlags.left; + bool isMovingRight = movementInfo.movementFlags.right; + bool isGrounded = movementInfo.movementFlags.grounded; + + if (isAlive && (isGrounded /* || (canControlInAir && isMoving))*/)) + { + f32 spineOrientation = 0.0f; + f32 headOrientation = 0.0f; + f32 waistOrientation = 0.0f; + + if (isMovingForward) + { + if (isMovingRight) + { + spineOrientation = 30.0f; + headOrientation = -15.0f; + waistOrientation = 45.0f; + } + else if (isMovingLeft) + { + spineOrientation = -30.0f; + headOrientation = 15.0f; + waistOrientation = -45.0f; + } + } + else if (isMovingBackward) + { + if (isMovingRight) + { + spineOrientation = -30.0f; + headOrientation = 15.0f; + waistOrientation = -45.0f; + } + else if (isMovingLeft) + { + spineOrientation = 30.0f; + headOrientation = -15.0f; + waistOrientation = 45.0f; + } + } + else if (isMovingRight) + { + spineOrientation = 45.0f; + headOrientation = -30.0f; + waistOrientation = 90.0f; + } + else if (isMovingLeft) + { + spineOrientation = -45.0f; + headOrientation = 30.0f; + waistOrientation = -90.0f; + } + + SetOrientation(networkedEntity.spineRotationSettings, spineOrientation); + SetOrientation(networkedEntity.headRotationSettings, headOrientation); + SetOrientation(networkedEntity.waistRotationSettings, waistOrientation); + } + + auto HandleUpdateOrientation = [](vec4& settings, f32 deltaTime) -> bool + { + if (settings.x == settings.y) + return false; + + settings.w += deltaTime; + settings.w = glm::clamp(settings.w, 0.0f, settings.z); + + f32 progress = settings.w / settings.z; + settings.x = glm::mix(settings.x, settings.y, progress); + + return true; + }; + + if (HandleUpdateOrientation(networkedEntity.spineRotationSettings, deltaTime)) + { + quat rotation = glm::quat(glm::vec3(0.0f, glm::radians(networkedEntity.spineRotationSettings.x), 0.0f)); + animationSystem->SetBoneRotation(instanceID, Animation::Bone::SpineLow, rotation); + } + if (HandleUpdateOrientation(networkedEntity.headRotationSettings, deltaTime)) + { + quat rotation = glm::quat(glm::vec3(0.0f, glm::radians(networkedEntity.headRotationSettings.x), 0.0f)); + animationSystem->SetBoneRotation(instanceID, Animation::Bone::Head, rotation); + } + if (HandleUpdateOrientation(networkedEntity.waistRotationSettings, deltaTime)) + { + quat rotation = glm::quat(glm::vec3(0.0f, glm::radians(networkedEntity.waistRotationSettings.x), 0.0f)); + animationSystem->SetBoneRotation(instanceID, Animation::Bone::Waist, rotation); + } + } + } + }); + } +} \ No newline at end of file diff --git a/Source/Game/Game/ECS/Systems/UpdateNetworkedEntity.h b/Source/Game/Game/ECS/Systems/UpdateNetworkedEntity.h new file mode 100644 index 00000000..a3bd1b0c --- /dev/null +++ b/Source/Game/Game/ECS/Systems/UpdateNetworkedEntity.h @@ -0,0 +1,13 @@ +#pragma once +#include +#include + +namespace ECS::Systems +{ + class UpdateNetworkedEntity + { + public: + static void Init(entt::registry& registry); + static void Update(entt::registry& registry, f32 deltaTime); + }; +} \ No newline at end of file diff --git a/Source/Game/Game/ECS/Systems/UpdateSkyboxes.cpp b/Source/Game/Game/ECS/Systems/UpdateSkyboxes.cpp index 586517dc..8725a7ab 100644 --- a/Source/Game/Game/ECS/Systems/UpdateSkyboxes.cpp +++ b/Source/Game/Game/ECS/Systems/UpdateSkyboxes.cpp @@ -22,7 +22,7 @@ namespace ECS::Systems void UpdateSkyboxes::Init(entt::registry& registry) { entt::registry::context& ctx = registry.ctx(); - Singletons::Skybox& skybox = ctx.emplace(); + auto& skybox = ctx.emplace(); GameRenderer* gameRenderer = ServiceLocator::GetGameRenderer(); ModelLoader* modelLoader = gameRenderer->GetModelLoader(); @@ -42,8 +42,8 @@ namespace ECS::Systems auto& transformSystem = ECS::TransformSystem::Get(registry); entt::registry::context& ctx = registry.ctx(); - Singletons::ActiveCamera& activeCamera = ctx.get(); - Components::Transform& cameraTransform = registry.get(activeCamera.entity); + auto& activeCamera = ctx.get(); + auto& cameraTransform = registry.get(activeCamera.entity); vec3 cameraPosition = cameraTransform.GetWorldPosition(); auto view = registry.view(); diff --git a/Source/Game/Game/ECS/Util/CameraUtil.cpp b/Source/Game/Game/ECS/Util/CameraUtil.cpp index 64a652a5..4a45fc70 100644 --- a/Source/Game/Game/ECS/Util/CameraUtil.cpp +++ b/Source/Game/Game/ECS/Util/CameraUtil.cpp @@ -22,9 +22,9 @@ namespace ECS::Util entt::registry* registry = ServiceLocator::GetEnttRegistries()->gameRegistry; entt::registry::context& ctx = registry->ctx(); - ECS::Singletons::ActiveCamera& activeCamera = ctx.get(); - ECS::Singletons::FreeflyingCameraSettings& freeFlyingCameraSettings = ctx.get(); - ECS::Singletons::OrbitalCameraSettings& orbitalCameraSettings = ctx.get(); + auto& activeCamera = ctx.get(); + auto& freeFlyingCameraSettings = ctx.get(); + auto& orbitalCameraSettings = ctx.get(); if (activeCamera.entity == freeFlyingCameraSettings.entity) { @@ -57,13 +57,13 @@ namespace ECS::Util entt::registry* registry = ServiceLocator::GetEnttRegistries()->gameRegistry; entt::registry::context& ctx = registry->ctx(); - ECS::Singletons::ActiveCamera& activeCamera = ctx.get(); + auto& activeCamera = ctx.get(); if (activeCamera.entity == entt::null) return; - ECS::Components::Transform& transform = registry->get(activeCamera.entity); - ECS::Components::Camera& camera = registry->get(activeCamera.entity); + auto& transform = registry->get(activeCamera.entity); + auto& camera = registry->get(activeCamera.entity); f32 fovInRadians = glm::radians(camera.fov) / sqrt(1.0f + (camera.aspectRatio * camera.aspectRatio)); diff --git a/Source/Game/Game/ECS/Util/CharSectionUtil.cpp b/Source/Game/Game/ECS/Util/CharSectionUtil.cpp new file mode 100644 index 00000000..0ff765c6 --- /dev/null +++ b/Source/Game/Game/ECS/Util/CharSectionUtil.cpp @@ -0,0 +1,44 @@ +#include "CharSectionUtil.h" +#include "Game/ECS/Singletons/CharSectionSingleton.h" +#include "Game/ECS/Singletons/ClientDBCollection.h" + +#include + +#include + +namespace ECS::Util +{ + namespace CharSection + { + void RefreshData(entt::registry& registry) + { + entt::registry::context& ctx = registry.ctx(); + + if (!ctx.find()) + { + ctx.emplace(); + } + + auto& charSectionSingleton = ctx.get(); + charSectionSingleton.keyToCharSectionID.clear(); + + auto& clientDBCollection = ctx.get(); + auto* charSectionStorage = clientDBCollection.Get(Singletons::ClientDBHash::CharSection); + + if (!charSectionStorage) + return; + + charSectionStorage->Each([&](const auto* storage, const ClientDB::Definitions::CharSection* charSection) + { + u32 key = charSection->raceID; + key |= charSection->sexID << 4; + key |= charSection->baseSection << 5; + key |= charSection->varationIndex << 9; + key |= charSection->colorIndex << 14; + key |= charSection->flags << 19; + + charSectionSingleton.keyToCharSectionID[key] = charSection->id; + }); + } + } +} \ No newline at end of file diff --git a/Source/Game/Game/ECS/Util/CharSectionUtil.h b/Source/Game/Game/ECS/Util/CharSectionUtil.h new file mode 100644 index 00000000..d119d2f5 --- /dev/null +++ b/Source/Game/Game/ECS/Util/CharSectionUtil.h @@ -0,0 +1,12 @@ +#pragma once +#include + +#include + +namespace ECS::Util +{ + namespace CharSection + { + void RefreshData(entt::registry& registry); + } +} \ No newline at end of file diff --git a/Source/Game/Game/Editor/AssetBrowser.cpp b/Source/Game/Game/Editor/AssetBrowser.cpp index 260ac8f2..0e22a956 100644 --- a/Source/Game/Game/Editor/AssetBrowser.cpp +++ b/Source/Game/Game/Editor/AssetBrowser.cpp @@ -218,12 +218,12 @@ namespace Editor entt::registry::context& ctx = registry.ctx(); auto& tSystem = ECS::TransformSystem::Get(registry); - ECS::Singletons::ActiveCamera& activeCamera = ctx.get(); + auto& activeCamera = ctx.get(); if (activeCamera.entity == entt::null) return; - ECS::Components::Transform& cameraTransform = registry.get(activeCamera.entity); + auto& cameraTransform = registry.get(activeCamera.entity); entt::entity entity = registry.create(); registry.emplace(entity); diff --git a/Source/Game/Game/Editor/CameraInfo.cpp b/Source/Game/Game/Editor/CameraInfo.cpp index 5004c2f7..f6bb76e5 100644 --- a/Source/Game/Game/Editor/CameraInfo.cpp +++ b/Source/Game/Game/Editor/CameraInfo.cpp @@ -40,16 +40,15 @@ namespace Editor entt::registry& registry = *registries->gameRegistry; entt::registry::context& ctx = registry.ctx(); - ActiveCamera& activeCamera = ctx.get(); - FreeflyingCameraSettings& settings = ctx.get(); + auto& activeCamera = ctx.get(); + auto& settings = ctx.get(); - // Print position if (ImGui::Begin(GetName())) { if (activeCamera.entity != entt::null) { - ECS::Components::Transform& cameraTransform = registry.get(activeCamera.entity); - ECS::Components::Camera& camera = registry.get(activeCamera.entity); + auto& cameraTransform = registry.get(activeCamera.entity); + auto& camera = registry.get(activeCamera.entity); quat rotQuat = quat(vec3(glm::radians(camera.pitch), glm::radians(camera.yaw), glm::radians(camera.roll))); diff --git a/Source/Game/Game/Editor/EditorHandler.cpp b/Source/Game/Game/Editor/EditorHandler.cpp index 72e42c38..e8a85fcc 100644 --- a/Source/Game/Game/Editor/EditorHandler.cpp +++ b/Source/Game/Game/Editor/EditorHandler.cpp @@ -4,6 +4,7 @@ #include "CameraInfo.h" #include "PerformanceDiagnostics.h" #include "MapSelector.h" +#include "NetworkedInfo.h" #include "SkyboxSelector.h" #include "TerrainTools.h" #include "Inspector.h" @@ -44,6 +45,7 @@ namespace Editor _editors.push_back(new CameraInfo()); _editors.push_back(new PerformanceDiagnostics()); _editors.push_back(new MapSelector()); + _editors.push_back(new NetworkedInfo()); _editors.push_back(new SkyboxSelector); _editors.push_back(new EaseCurveTool()); diff --git a/Source/Game/Game/Editor/Hierarchy.cpp b/Source/Game/Game/Editor/Hierarchy.cpp index 462bd36d..19ebd15b 100644 --- a/Source/Game/Game/Editor/Hierarchy.cpp +++ b/Source/Game/Game/Editor/Hierarchy.cpp @@ -130,7 +130,7 @@ namespace Editor if (!registry->all_of(entity)) return; - ECS::Components::WorldAABB& worldAABB = registry->get(entity); + auto& worldAABB = registry->get(entity); vec3 position = (worldAABB.min + worldAABB.max) * 0.5f; f32 radius = glm::distance(worldAABB.min, worldAABB.max) * 0.5f; diff --git a/Source/Game/Game/Editor/Inspector.cpp b/Source/Game/Game/Editor/Inspector.cpp index ea9581ba..a6183b85 100644 --- a/Source/Game/Game/Editor/Inspector.cpp +++ b/Source/Game/Game/Editor/Inspector.cpp @@ -478,12 +478,12 @@ namespace Editor { entt::registry::context& ctx = registry->ctx(); - ECS::Singletons::ActiveCamera& activeCamera = ctx.get(); + auto& activeCamera = ctx.get(); if (activeCamera.entity == entt::null) return false; - ECS::Components::Camera& camera = registry->get(static_cast(0)); + auto& camera = registry->get(static_cast(0)); mat4x4& viewMatrix = camera.worldToView; mat4x4& projMatrix = camera.viewToClip; diff --git a/Source/Game/Game/Editor/NetworkedInfo.cpp b/Source/Game/Game/Editor/NetworkedInfo.cpp new file mode 100644 index 00000000..9a5516eb --- /dev/null +++ b/Source/Game/Game/Editor/NetworkedInfo.cpp @@ -0,0 +1,230 @@ +#include "NetworkedInfo.h" + +#include "Game/Application/EnttRegistries.h" +#include "Game/ECS/Components/Camera.h" +#include "Game/ECS/Components/MovementInfo.h" +#include "Game/ECS/Components/UnitStatsComponent.h" +#include "Game/ECS/Singletons/ActiveCamera.h" +#include "Game/ECS/Singletons/CameraSaveDB.h" +#include "Game/ECS/Singletons/CharacterSingleton.h" +#include "Game/ECS/Singletons/ClientDBCollection.h" +#include "Game/ECS/Singletons/FreeflyingCameraSettings.h" +#include "Game/ECS/Singletons/NetworkState.h" +#include "Game/ECS/Util/Transforms.h" +#include "Game/Util/CameraSaveUtil.h" +#include "Game/Util/CameraSaveUtil.h" +#include "Game/Util/MapUtil.h" + +#include +#include + +#include + +#include + +#include +#include +#include +#include + +#include + +using namespace ClientDB; +using namespace ECS::Singletons; + +namespace Editor +{ + NetworkedInfo::NetworkedInfo() + : BaseEditor(GetName(), true) + { + + } + + void NetworkedInfo::DrawImGui() + { + if (ImGui::Begin(GetName())) + { + EnttRegistries* registries = ServiceLocator::GetEnttRegistries(); + entt::registry& registry = *registries->gameRegistry; + entt::registry::context& ctx = registry.ctx(); + + auto& networkState = ctx.get(); + auto& characterSingleton = ctx.get(); + + bool isConnected = networkState.client->IsConnected(); + + ImGui::Text("Connection Status : %s", isConnected ? "Connected" : "Disconnected"); + ImGui::NewLine(); + ImGui::Separator(); + + if (characterSingleton.entity != entt::null) + { + auto& characterTransform = registry.get(characterSingleton.entity); + auto& movementInfo = registry.get(characterSingleton.modelEntity); + + glm::vec3 worldPos = characterTransform.GetWorldPosition(); + const vec3 characterForward = characterTransform.GetLocalForward(); + const vec3 characterRight = characterTransform.GetLocalRight(); + const vec3 characterUp = characterTransform.GetLocalUp(); + + f32 yaw = glm::degrees(movementInfo.yaw - glm::pi()); + ImGui::Text("Pos + O: (%.2f, %.2f, %.2f, %.2f)", worldPos.x, worldPos.y, worldPos.z, yaw); + ImGui::Text("Speed: %.2f", movementInfo.speed); + + ImGui::Separator(); + + ImGui::Text("Pitch, Yaw, Roll: (%.2f, %.2f, %.2f)", glm::degrees(movementInfo.pitch), yaw, 0.0f); + ImGui::Text("Forward: (%.2f, %.2f, %.2f)", characterForward.x, characterForward.y, characterForward.z); + ImGui::Text("Right: (%.2f, %.2f, %.2f)", characterRight.x, characterRight.y, characterRight.z); + ImGui::Text("Up: (%.2f, %.2f, %.2f)", characterUp.x, characterUp.y, characterUp.z); + + ImGui::NewLine(); + ImGui::Separator(); + + if (networkState.client->IsConnected()) + { + if (ImGui::CollapsingHeader("Basic Info")) + { + auto& unitStatsComponent = registry.get(characterSingleton.modelEntity); + ImGui::Text("Health (Base, Current, Max) : (%.2f, %.2f, %.2f)", unitStatsComponent.baseHealth, unitStatsComponent.currentHealth, unitStatsComponent.maxHealth); + + for (u32 i = 0; i < (u32)ECS::Components::PowerType::Count; i++) + { + ECS::Components::PowerType type = (ECS::Components::PowerType)i; + + switch (type) + { + case ECS::Components::PowerType::Mana: + { + ImGui::Text("Mana (Base, Current, Max) : (%.2f, %.2f, %.2f)", unitStatsComponent.basePower[i], unitStatsComponent.currentPower[i], unitStatsComponent.maxPower[i]); + break; + } + case ECS::Components::PowerType::Rage: + { + ImGui::Text("Rage (Base, Current, Max) : (%.2f, %.2f, %.2f)", unitStatsComponent.basePower[i], unitStatsComponent.currentPower[i], unitStatsComponent.maxPower[i]); + break; + } + case ECS::Components::PowerType::Focus: + { + ImGui::Text("Focus (Base, Current, Max) : (%.2f, %.2f, %.2f)", unitStatsComponent.basePower[i], unitStatsComponent.currentPower[i], unitStatsComponent.maxPower[i]); + break; + } + case ECS::Components::PowerType::Energy: + { + ImGui::Text("Energy (Base, Current, Max) : (%.2f, %.2f, %.2f)", unitStatsComponent.basePower[i], unitStatsComponent.currentPower[i], unitStatsComponent.maxPower[i]); + break; + } + case ECS::Components::PowerType::Happiness: + { + ImGui::Text("Happiness (Base, Current, Max) : (%.2f, %.2f, %.2f)", unitStatsComponent.basePower[i], unitStatsComponent.currentPower[i], unitStatsComponent.maxPower[i]); + break; + } + } + } + + ImGui::Separator(); + } + + if (characterSingleton.targetEntity != entt::null && registry.valid(characterSingleton.targetEntity)) + { + if (ImGui::CollapsingHeader("Target Info")) + { + auto& unitStatsComponent = registry.get(characterSingleton.targetEntity); + ImGui::Text("Health (Base, Current, Max) : (%.2f, %.2f, %.2f)", unitStatsComponent.baseHealth, unitStatsComponent.currentHealth, unitStatsComponent.maxHealth); + + for (u32 i = 0; i < (u32)ECS::Components::PowerType::Count; i++) + { + ECS::Components::PowerType type = (ECS::Components::PowerType)i; + + switch (type) + { + case ECS::Components::PowerType::Mana: + { + ImGui::Text("Mana (Base, Current, Max) : (%.2f, %.2f, %.2f)", unitStatsComponent.basePower[i], unitStatsComponent.currentPower[i], unitStatsComponent.maxPower[i]); + break; + } + case ECS::Components::PowerType::Rage: + { + ImGui::Text("Rage (Base, Current, Max) : (%.2f, %.2f, %.2f)", unitStatsComponent.basePower[i], unitStatsComponent.currentPower[i], unitStatsComponent.maxPower[i]); + break; + } + case ECS::Components::PowerType::Focus: + { + ImGui::Text("Focus (Base, Current, Max) : (%.2f, %.2f, %.2f)", unitStatsComponent.basePower[i], unitStatsComponent.currentPower[i], unitStatsComponent.maxPower[i]); + break; + } + case ECS::Components::PowerType::Energy: + { + ImGui::Text("Energy (Base, Current, Max) : (%.2f, %.2f, %.2f)", unitStatsComponent.basePower[i], unitStatsComponent.currentPower[i], unitStatsComponent.maxPower[i]); + break; + } + case ECS::Components::PowerType::Happiness: + { + ImGui::Text("Happiness (Base, Current, Max) : (%.2f, %.2f, %.2f)", unitStatsComponent.basePower[i], unitStatsComponent.currentPower[i], unitStatsComponent.maxPower[i]); + break; + } + } + } + + ImGui::Separator(); + } + } + + if (ImGui::Button("Disconnect")) + { + networkState.client->Close(); + } + } + else + { + ImGui::Text("Not connected to server"); + ImGui::Text("Character Name"); + ImGui::InputText("##CharacterNameInputField", &networkState.characterName); + + if (ImGui::Button("Connect")) + { + if (networkState.client && !networkState.characterName.empty()) + { + Network::Socket::Result initResult = networkState.client->Init(Network::Socket::Mode::TCP); + if (initResult == Network::Socket::Result::SUCCESS) + { + // Connect to IP/Port + std::string ipAddress = "127.0.0.1"; + u16 port = 4000; + + Network::Socket::Result connectResult = networkState.client->Connect(ipAddress, port); + + if (connectResult != Network::Socket::Result::SUCCESS && + connectResult != Network::Socket::Result::ERROR_WOULD_BLOCK) + { + NC_LOG_ERROR("Network : Failed to connect to ({0}, {1})", ipAddress, port); + } + else + { + networkState.client->GetSocket()->SetBlockingState(false); + + std::shared_ptr buffer = Bytebuffer::Borrow<128>(); + Network::PacketHeader header = + { + .opcode = Network::Opcode::CMSG_CONNECTED, + .size = static_cast(networkState.characterName.size()) + 1u + }; + + buffer->Put(header); + buffer->PutString(networkState.characterName); + + networkState.client->Send(buffer); + } + } + } + } + } + } + else + { + ImGui::Text("No active character"); + } + } + ImGui::End(); + } +} \ No newline at end of file diff --git a/Source/Game/Game/Editor/NetworkedInfo.h b/Source/Game/Game/Editor/NetworkedInfo.h new file mode 100644 index 00000000..83e67dd0 --- /dev/null +++ b/Source/Game/Game/Editor/NetworkedInfo.h @@ -0,0 +1,19 @@ +#pragma once +#include "BaseEditor.h" + +namespace Editor +{ + class NetworkedInfo : public BaseEditor + { + public: + NetworkedInfo(); + + virtual const char* GetName() override { return "Networked Info"; } + + virtual void DrawImGui() override; + + private: + + private: + }; +} \ No newline at end of file diff --git a/Source/Game/Game/Editor/SkyboxSelector.cpp b/Source/Game/Game/Editor/SkyboxSelector.cpp index 93cf814b..708fd7b5 100644 --- a/Source/Game/Game/Editor/SkyboxSelector.cpp +++ b/Source/Game/Game/Editor/SkyboxSelector.cpp @@ -127,10 +127,10 @@ namespace Editor entt::registry& registry = *registries->gameRegistry; entt::registry::context& ctx = registry.ctx(); - ECS::Singletons::Skybox& skybox = ctx.get(); + auto& skybox = ctx.get(); std::string loadedSkyboxPath = "NONE"; - ECS::Components::Name& name = registry.get(skybox.entity); + auto& name = registry.get(skybox.entity); if (name.fullName != "") { loadedSkyboxPath = name.fullName; @@ -242,7 +242,7 @@ namespace Editor { ModelLoader* modelLoader = ServiceLocator::GetGameRenderer()->GetModelLoader(); - ECS::Components::Model& model = registry.get(skybox.entity); + auto& model = registry.get(skybox.entity); modelLoader->UnloadModelForEntity(skybox.entity, model.modelID); } diff --git a/Source/Game/Game/Editor/Viewport.cpp b/Source/Game/Game/Editor/Viewport.cpp index 28ec2158..05e672de 100644 --- a/Source/Game/Game/Editor/Viewport.cpp +++ b/Source/Game/Game/Editor/Viewport.cpp @@ -43,7 +43,7 @@ namespace Editor entt::registry* registry = ServiceLocator::GetEnttRegistries()->gameRegistry; entt::registry::context& ctx = registry->ctx(); - ECS::Singletons::FreeflyingCameraSettings& cameraSettings = ctx.get(); + auto& cameraSettings = ctx.get(); if (cameraSettings.captureMouse) return false; @@ -147,7 +147,7 @@ namespace Editor entt::registry* registry = ServiceLocator::GetEnttRegistries()->gameRegistry; entt::registry::context& ctx = registry->ctx(); - ECS::Singletons::FreeflyingCameraSettings& cameraSettings = ctx.get(); + auto& cameraSettings = ctx.get(); ImGuiIO& io = ImGui::GetIO(); @@ -244,7 +244,8 @@ namespace Editor entt::registry* registry = ServiceLocator::GetEnttRegistries()->gameRegistry; entt::registry::context& ctx = registry->ctx(); - ECS::Singletons::FreeflyingCameraSettings& settings = ctx.get(); + auto& settings = ctx.get(); + ImGui::SameLine(); ImGui::Text("Camera Speed: %.1f", settings.cameraSpeed); } diff --git a/Source/Game/Game/Gameplay/GameConsole/GameConsoleCommandHandler.cpp b/Source/Game/Game/Gameplay/GameConsole/GameConsoleCommandHandler.cpp index aeab1ae8..3867fcf1 100644 --- a/Source/Game/Game/Gameplay/GameConsole/GameConsoleCommandHandler.cpp +++ b/Source/Game/Game/Gameplay/GameConsole/GameConsoleCommandHandler.cpp @@ -18,6 +18,13 @@ GameConsoleCommandHandler::GameConsoleCommandHandler() RegisterCommand("savecamera", GameConsoleCommands::HandleSaveCamera); RegisterCommand("loadcamera", GameConsoleCommands::HandleLoadCamera); RegisterCommand("clearmap", GameConsoleCommands::HandleClearMap); + RegisterCommand("cast", GameConsoleCommands::HandleCast); + RegisterCommand("damage", GameConsoleCommands::HandleDamage); + RegisterCommand("kill", GameConsoleCommands::HandleKill); + RegisterCommand("revive", GameConsoleCommands::HandleRevive); + RegisterCommand("morph", GameConsoleCommands::HandleMorph); + RegisterCommand("createchar", GameConsoleCommands::HandleCreateChar); + RegisterCommand("deletechar", GameConsoleCommands::HandleDeleteChar); } bool GameConsoleCommandHandler::HandleCommand(GameConsole* gameConsole, std::string& command) diff --git a/Source/Game/Game/Gameplay/GameConsole/GameConsoleCommands.cpp b/Source/Game/Game/Gameplay/GameConsole/GameConsoleCommands.cpp index 9d189a3c..29664f2c 100644 --- a/Source/Game/Game/Gameplay/GameConsole/GameConsoleCommands.cpp +++ b/Source/Game/Game/Gameplay/GameConsole/GameConsoleCommands.cpp @@ -3,7 +3,11 @@ #include "GameConsoleCommandHandler.h" #include "Game/Application/EnttRegistries.h" #include "Game/ECS/Components/Camera.h" +#include "Game/ECS/Components/CastInfo.h" +#include "Game/ECS/Components/UnitStatsComponent.h" #include "Game/ECS/Util/Transforms.h" +#include "Game/ECS/Singletons/CharacterSingleton.h" +#include "Game/ECS/Singletons/ClientDBCollection.h" #include "Game/ECS/Singletons/NetworkState.h" #include "Game/Gameplay/MapLoader.h" #include "Game/Scripting/LuaManager.h" @@ -14,8 +18,8 @@ #include -#include #include +#include #include #include @@ -79,11 +83,7 @@ bool GameConsoleCommands::HandleDoString(GameConsoleCommandHandler* commandHandl code += subCommands[i]; } - if (!ServiceLocator::GetLuaManager()->DoString(code)) - { - gameConsole->PrintError("Failed to run Lua DoString"); - } - + ServiceLocator::GetLuaManager()->DoString(code); return true; } @@ -181,4 +181,239 @@ bool GameConsoleCommands::HandleClearMap(GameConsoleCommandHandler* commandHandl mapLoader->UnloadMap(); return false; -} \ No newline at end of file +} + +bool GameConsoleCommands::HandleCast(GameConsoleCommandHandler* commandHandler, GameConsole* gameConsole, std::vector& subCommands) +{ + entt::registry* registry = ServiceLocator::GetEnttRegistries()->gameRegistry; + ECS::Singletons::CharacterSingleton& characterSingleton = registry->ctx().get(); + ECS::Singletons::NetworkState& networkState = registry->ctx().get(); + + if (networkState.client->IsConnected()) + { + std::shared_ptr buffer = Bytebuffer::Borrow<128>(); + Network::PacketHeader header = + { + .opcode = Network::Opcode::CMSG_REQUEST_SPELLCAST, + .size = sizeof(u32) + }; + + buffer->Put(header); + buffer->PutU32(0); + + networkState.client->Send(buffer); + } + else + { + auto& castInfo = registry->emplace_or_replace(characterSingleton.modelEntity); + castInfo.target = characterSingleton.targetEntity; + castInfo.castTime = 1.0f; + castInfo.duration = 0.0f; + } + + return false; +} + +bool GameConsoleCommands::HandleDamage(GameConsoleCommandHandler* commandHandler, GameConsole* gameConsole, std::vector& subCommands) +{ + entt::registry* registry = ServiceLocator::GetEnttRegistries()->gameRegistry; + ECS::Singletons::CharacterSingleton& characterSingleton = registry->ctx().get(); + ECS::Singletons::NetworkState& networkState = registry->ctx().get(); + + if (networkState.client->IsConnected()) + { + std::shared_ptr buffer = Bytebuffer::Borrow<128>(); + Network::PacketHeader header = + { + .opcode = Network::Opcode::CMSG_CHEAT_DAMAGE, + .size = sizeof(f32) + }; + + buffer->Put(header); + buffer->PutF32(35); + + networkState.client->Send(buffer); + } + else + { + auto& unitStatsComponent = registry->get(characterSingleton.modelEntity); + unitStatsComponent.currentHealth = glm::max(unitStatsComponent.currentHealth - 25.0f, 0.0f); + } + + return false; +} + +bool GameConsoleCommands::HandleKill(GameConsoleCommandHandler* commandHandler, GameConsole* gameConsole, std::vector& subCommands) +{ + entt::registry* registry = ServiceLocator::GetEnttRegistries()->gameRegistry; + ECS::Singletons::CharacterSingleton& characterSingleton = registry->ctx().get(); + ECS::Singletons::NetworkState& networkState = registry->ctx().get(); + + if (networkState.client->IsConnected()) + { + std::shared_ptr buffer = Bytebuffer::Borrow<128>(); + Network::PacketHeader header = + { + .opcode = Network::Opcode::CMSG_CHEAT_KILL, + .size = 0 + }; + + buffer->Put(header); + + networkState.client->Send(buffer); + } + else + { + auto& unitStatsComponent = registry->get(characterSingleton.modelEntity); + unitStatsComponent.currentHealth = 0.0f; + } + + return false; +} + +bool GameConsoleCommands::HandleRevive(GameConsoleCommandHandler* commandHandler, GameConsole* gameConsole, std::vector& subCommands) +{ + entt::registry* registry = ServiceLocator::GetEnttRegistries()->gameRegistry; + ECS::Singletons::CharacterSingleton& characterSingleton = registry->ctx().get(); + ECS::Singletons::NetworkState& networkState = registry->ctx().get(); + + if (networkState.client->IsConnected()) + { + std::shared_ptr buffer = Bytebuffer::Borrow<128>(); + Network::PacketHeader header = + { + .opcode = Network::Opcode::CMSG_CHEAT_RESURRECT, + .size = 0 + }; + + buffer->Put(header); + + networkState.client->Send(buffer); + } + else + { + auto& unitStatsComponent = registry->get(characterSingleton.modelEntity); + unitStatsComponent.currentHealth = unitStatsComponent.maxHealth; + } + + return false; +} + +bool GameConsoleCommands::HandleMorph(GameConsoleCommandHandler* commandHandler, GameConsole* gameConsole, std::vector& subCommands) +{ + if (subCommands.size() == 0) + return false; + + const std::string morphIDAsString = subCommands[0]; + const u32 displayID = std::stoi(morphIDAsString); + + entt::registry* registry = ServiceLocator::GetEnttRegistries()->gameRegistry; + auto& clientDBCollection = registry->ctx().get(); + auto* creatureDisplayStorage = clientDBCollection.Get(ECS::Singletons::ClientDBHash::CreatureDisplayInfo); + + if (!creatureDisplayStorage || !creatureDisplayStorage->HasRow(displayID)) + return false; + + ECS::Singletons::NetworkState& networkState = registry->ctx().get(); + + if (networkState.client->IsConnected()) + { + std::shared_ptr buffer = Bytebuffer::Borrow<128>(); + Network::PacketHeader header = + { + .opcode = Network::Opcode::CMSG_CHEAT_MORPH, + .size = 4 + }; + + buffer->Put(header); + buffer->Put(displayID); + + networkState.client->Send(buffer); + } + else + { + auto& characterSingleton = registry->ctx().get(); + ModelLoader* modelLoader = ServiceLocator::GetGameRenderer()->GetModelLoader(); + + if (!modelLoader->LoadDisplayIDForEntity(characterSingleton.modelEntity, displayID)) + return false; + + gameConsole->PrintSuccess("Morphed into : %u", displayID); + } + + return true; +} + +bool GameConsoleCommands::HandleCreateChar(GameConsoleCommandHandler* commandHandler, GameConsole* gameConsole, std::vector& subCommands) +{ + if (subCommands.size() == 0) + return false; + + const std::string characterName = subCommands[0]; + if (!StringUtils::StringIsAlphaAndAtLeastLength(characterName, 2)) + { + gameConsole->PrintError("Failed to send Create Character, name supplied is invalid : %s", characterName.c_str()); + return true; + } + + entt::registry* registry = ServiceLocator::GetEnttRegistries()->gameRegistry; + ECS::Singletons::NetworkState& networkState = registry->ctx().get(); + + if (networkState.client->IsConnected()) + { + std::shared_ptr buffer = Bytebuffer::Borrow<128>(); + Network::PacketHeader header = + { + .opcode = Network::Opcode::CMSG_CHEAT_CREATE_CHARACTER, + .size = static_cast(characterName.size()) + 1u + }; + + buffer->Put(header); + buffer->PutString(characterName); + + networkState.client->Send(buffer); + } + else + { + gameConsole->PrintWarning("Fialed to send Create Character, not connected"); + } + + return true; +} + +bool GameConsoleCommands::HandleDeleteChar(GameConsoleCommandHandler* commandHandler, GameConsole* gameConsole, std::vector& subCommands) +{ + if (subCommands.size() == 0) + return false; + + const std::string characterName = subCommands[0]; + if (!StringUtils::StringIsAlphaAndAtLeastLength(characterName, 2)) + { + gameConsole->PrintError("Failed to send Delete Character, name supplied is invalid : %s", characterName.c_str()); + return true; + } + + entt::registry* registry = ServiceLocator::GetEnttRegistries()->gameRegistry; + ECS::Singletons::NetworkState& networkState = registry->ctx().get(); + + if (networkState.client->IsConnected()) + { + std::shared_ptr buffer = Bytebuffer::Borrow<128>(); + Network::PacketHeader header = + { + .opcode = Network::Opcode::CMSG_CHEAT_DELETE_CHARACTER, + .size = static_cast(characterName.size()) + 1u + }; + + buffer->Put(header); + buffer->PutString(characterName); + + networkState.client->Send(buffer); + } + else + { + gameConsole->PrintWarning("Fialed to send Delete Character, not connected"); + } + + return true; +} diff --git a/Source/Game/Game/Gameplay/GameConsole/GameConsoleCommands.h b/Source/Game/Game/Gameplay/GameConsole/GameConsoleCommands.h index 8c4892a2..595ef20b 100644 --- a/Source/Game/Game/Gameplay/GameConsole/GameConsoleCommands.h +++ b/Source/Game/Game/Gameplay/GameConsole/GameConsoleCommands.h @@ -15,4 +15,11 @@ class GameConsoleCommands static bool HandleSaveCamera(GameConsoleCommandHandler* commandHandler, GameConsole* gameConsole, std::vector& subCommands); static bool HandleLoadCamera(GameConsoleCommandHandler* commandHandler, GameConsole* gameConsole, std::vector& subCommands); static bool HandleClearMap(GameConsoleCommandHandler* commandHandler, GameConsole* gameConsole, std::vector& subCommands); + static bool HandleCast(GameConsoleCommandHandler* commandHandler, GameConsole* gameConsole, std::vector& subCommands); + static bool HandleDamage(GameConsoleCommandHandler* commandHandler, GameConsole* gameConsole, std::vector& subCommands); + static bool HandleKill(GameConsoleCommandHandler* commandHandler, GameConsole* gameConsole, std::vector& subCommands); + static bool HandleRevive(GameConsoleCommandHandler* commandHandler, GameConsole* gameConsole, std::vector& subCommands); + static bool HandleMorph(GameConsoleCommandHandler* commandHandler, GameConsole* gameConsole, std::vector& subCommands); + static bool HandleCreateChar(GameConsoleCommandHandler* commandHandler, GameConsole* gameConsole, std::vector& subCommands); + static bool HandleDeleteChar(GameConsoleCommandHandler* commandHandler, GameConsole* gameConsole, std::vector& subCommands); }; \ No newline at end of file diff --git a/Source/Game/Game/Loaders/ClientDB/ClientDBLoader.h b/Source/Game/Game/Loaders/ClientDB/ClientDBLoader.h index 561dc41f..383013c1 100644 --- a/Source/Game/Game/Loaders/ClientDB/ClientDBLoader.h +++ b/Source/Game/Game/Loaders/ClientDB/ClientDBLoader.h @@ -184,6 +184,86 @@ class ClientDBLoader break; } + case ClientDBHash::AnimationData: + { + auto* db = new ClientDB::Storage(clientDBPair.fileName); + if (!db->Read(buffer)) + { + NC_LOG_ERROR("ClientDBLoader : Failed to load '{0}'. Could not read ClientDB from Buffer.", clientDBPair.fileName); + break; + } + + auto* rawDB = reinterpret_cast*>(db); + clientDBCollection._dbs.push_back(rawDB); + clientDBCollection._dbHashToIndex[hash] = index; + numLoadedClientDBs++; + break; + } + + case ClientDBHash::CreatureDisplayInfo: + { + auto* db = new ClientDB::Storage(clientDBPair.fileName); + if (!db->Read(buffer)) + { + NC_LOG_ERROR("ClientDBLoader : Failed to load '{0}'. Could not read ClientDB from Buffer.", clientDBPair.fileName); + break; + } + + auto* rawDB = reinterpret_cast*>(db); + clientDBCollection._dbs.push_back(rawDB); + clientDBCollection._dbHashToIndex[hash] = index; + numLoadedClientDBs++; + break; + } + + case ClientDBHash::CreatureDisplayInfoExtra: + { + auto* db = new ClientDB::Storage(clientDBPair.fileName); + if (!db->Read(buffer)) + { + NC_LOG_ERROR("ClientDBLoader : Failed to load '{0}'. Could not read ClientDB from Buffer.", clientDBPair.fileName); + break; + } + + auto* rawDB = reinterpret_cast*>(db); + clientDBCollection._dbs.push_back(rawDB); + clientDBCollection._dbHashToIndex[hash] = index; + numLoadedClientDBs++; + break; + } + + case ClientDBHash::CreatureModelData: + { + auto* db = new ClientDB::Storage(clientDBPair.fileName); + if (!db->Read(buffer)) + { + NC_LOG_ERROR("ClientDBLoader : Failed to load '{0}'. Could not read ClientDB from Buffer.", clientDBPair.fileName); + break; + } + + auto* rawDB = reinterpret_cast*>(db); + clientDBCollection._dbs.push_back(rawDB); + clientDBCollection._dbHashToIndex[hash] = index; + numLoadedClientDBs++; + break; + } + + case ClientDBHash::CharSection: + { + auto* db = new ClientDB::Storage(clientDBPair.fileName); + if (!db->Read(buffer)) + { + NC_LOG_ERROR("ClientDBLoader : Failed to load '{0}'. Could not read ClientDB from Buffer.", clientDBPair.fileName); + break; + } + + auto* rawDB = reinterpret_cast*>(db); + clientDBCollection._dbs.push_back(rawDB); + clientDBCollection._dbHashToIndex[hash] = index; + numLoadedClientDBs++; + break; + } + case ClientDBHash::CameraSave: { auto* db = new ClientDB::Storage(clientDBPair.fileName); diff --git a/Source/Game/Game/Loaders/Texture/TextureLoader.h b/Source/Game/Game/Loaders/Texture/TextureLoader.h index 838986a3..3912755e 100644 --- a/Source/Game/Game/Loaders/Texture/TextureLoader.h +++ b/Source/Game/Game/Loaders/Texture/TextureLoader.h @@ -32,7 +32,7 @@ class TextureLoader entt::registry* registry = registries->gameRegistry; entt::registry::context& ctx = registry->ctx(); - ECS::Singletons::TextureSingleton& textureSingleton = ctx.emplace(); + auto& textureSingleton = ctx.emplace(); static const fs::path fileExtension = ".dds"; fs::path relativeParentPath = "Data/Texture"; diff --git a/Source/Game/Game/Rendering/Model/ModelLoader.cpp b/Source/Game/Game/Rendering/Model/ModelLoader.cpp index 57fb8576..95907035 100644 --- a/Source/Game/Game/Rendering/Model/ModelLoader.cpp +++ b/Source/Game/Game/Rendering/Model/ModelLoader.cpp @@ -4,6 +4,7 @@ #include "Game/Application/EnttRegistries.h" #include "Game/ECS/Singletons/JoltState.h" #include "Game/ECS/Components/Name.h" +#include "Game/ECS/Components/NetworkedEntity.h" #include "Game/ECS/Components/Model.h" #include "Game/ECS/Singletons/Skybox.h" #include "Game/ECS/Util/Transforms.h" @@ -29,6 +30,7 @@ #include #include #include +#include namespace fs = std::filesystem; @@ -129,7 +131,7 @@ void ModelLoader::Init() if (_nameHashToDiscoveredModel.contains(discoveredModel.nameHash)) { const DiscoveredModel& existingDiscoveredModel = _nameHashToDiscoveredModel[discoveredModel.nameHash]; - NC_LOG_ERROR("Found duplicate model hash ({0}) for Paths (\"{1}\") - (\"{2}\")", discoveredModel.nameHash, existingDiscoveredModel.name.c_str(), discoveredModel.name.c_str()); + NC_LOG_ERROR("Found duplicate model hash ({0}) for Paths (\"{1}\") - (\"{2}\")", discoveredModel.nameHash, existingDiscoveredModel.name, discoveredModel.name); } _nameHashToDiscoveredModel[discoveredModel.nameHash] = discoveredModel; @@ -221,7 +223,7 @@ void ModelLoader::Clear() _createdEntities.clear(); entt::registry::context& ctx = registry->ctx(); - ECS::Singletons::Skybox& skybox = ctx.get(); + auto& skybox = ctx.get(); registry->get(skybox.entity).instanceID = std::numeric_limits::max(); } @@ -413,6 +415,8 @@ void ModelLoader::Update(f32 deltaTime) reserveInfo.numTextureTransforms += discoveredModel.modelHeader.numTextureTransforms * isSupported; } + bool hasDisplayID = request.displayID != std::numeric_limits().max(); + if (!_nameHashToLoadState.contains(nameHash)) { _nameHashToLoadState[nameHash] = LoadState::Received; @@ -422,11 +426,17 @@ void ModelLoader::Update(f32 deltaTime) reserveInfo.numModels++; reserveInfo.numVertices += discoveredModel.modelHeader.numVertices * isSupported; reserveInfo.numIndices += discoveredModel.modelHeader.numIndices * isSupported; - reserveInfo.numTextureUnits += discoveredModel.modelHeader.numTextureUnits * isSupported; + reserveInfo.numTextureUnits += discoveredModel.modelHeader.numTextureUnits + (discoveredModel.modelHeader.numTextureUnits * hasDisplayID) * isSupported; + reserveInfo.numDecorationSets += discoveredModel.modelHeader.numDecorationSets * isSupported; + reserveInfo.numDecorations += discoveredModel.modelHeader.numDecorations * isSupported; reserveInfo.numUniqueOpaqueDrawcalls += discoveredModel.modelHeader.numOpaqueRenderBatches * isSupported; reserveInfo.numUniqueTransparentDrawcalls += discoveredModel.modelHeader.numTransparentRenderBatches * isSupported; } + else + { + reserveInfo.numTextureUnits += discoveredModel.modelHeader.numTextureUnits * isSupported * hasDisplayID; + } } // Prepare lookup tables @@ -486,26 +496,24 @@ void ModelLoader::Update(f32 deltaTime) AddDynamicInstance(request.entity, request); } - entt::registry* registry = ServiceLocator::GetEnttRegistries()->gameRegistry; - mat4x4 identity = mat4x4(1.0f); - - for (u32 dynamicRequestID : unloadRequests) - { - LoadRequestInternal& request = _dynamicLoadRequests[dynamicRequestID]; - - ECS::Components::Model& model = registry->get(request.entity); - - if (model.modelID != request.placement.uniqueID) - continue; - - _modelRenderer->ModifyInstance(request.entity, model.instanceID, std::numeric_limits().max(), identity); - } - // Fit the buffers to the data we loaded _modelRenderer->FitBuffersAfterLoad(); animationSystem->FitToBuffersAfterLoad(); } } + + size_t unloadRequests = _unloadRequests.size_approx(); + if (unloadRequests) + { + entt::registry* registry = ServiceLocator::GetEnttRegistries()->gameRegistry; + mat4x4 identity = mat4x4(1.0f); + + UnloadRequest unloadRequest; + while (_unloadRequests.try_dequeue(unloadRequest)) + { + _modelRenderer->ModifyInstance(unloadRequest.entity, unloadRequest.instanceID, std::numeric_limits().max(), nullptr, identity); + } + } } entt::entity ModelLoader::CreateModelEntity(const std::string& name) @@ -560,14 +568,42 @@ void ModelLoader::LoadModelForEntity(entt::entity entity, u32 modelNameHash) _dynamicRequests.enqueue(loadRequest); } -void ModelLoader::UnloadModelForEntity(entt::entity entity, u32 modelID) +bool ModelLoader::LoadDisplayIDForEntity(entt::entity entity, u32 displayID) { LoadRequestInternal loadRequest; loadRequest.entity = entity; - loadRequest.placement.uniqueID = modelID; - loadRequest.placement.nameHash = std::numeric_limits().max(); + loadRequest.displayID = displayID; + + entt::registry* registry = ServiceLocator::GetEnttRegistries()->gameRegistry; + auto& clientDBCollection = registry->ctx().get(); + auto* creatureDisplayInfoStorage = clientDBCollection.Get(ECS::Singletons::ClientDBHash::CreatureDisplayInfo); + auto* creatureModelDataStorage = clientDBCollection.Get(ECS::Singletons::ClientDBHash::CreatureModelData); + + if (!creatureDisplayInfoStorage || !creatureModelDataStorage) + return false; + + const ClientDB::Definitions::CreatureDisplayInfo* creatureDisplayInfo = creatureDisplayInfoStorage->GetRow(displayID); + if (!creatureDisplayInfo) + return false; + const ClientDB::Definitions::CreatureModelData* creatureModelData = creatureModelDataStorage->GetRow(creatureDisplayInfo->modelID); + if (!creatureModelData) + return false; + + loadRequest.placement.nameHash = creatureModelData->modelHash; _dynamicRequests.enqueue(loadRequest); + return true; +} + +void ModelLoader::UnloadModelForEntity(entt::entity entity, u32 instanceID) +{ + UnloadRequest unloadRequest = + { + .entity = entity, + .instanceID = instanceID + }; + + _unloadRequests.enqueue(unloadRequest); } u32 ModelLoader::GetModelHashFromModelPath(const std::string& modelPath) @@ -703,22 +739,22 @@ void ModelLoader::AddStaticInstance(entt::entity entityID, const LoadRequestInte f32 scale = static_cast(request.placement.scale) / 1024.0f; tSystem.SetLocalTransform(entityID, request.placement.position, request.placement.rotation, vec3(scale, scale, scale)); - ECS::Components::Name& name = registry->get(entityID); + auto& name = registry->get(entityID); DiscoveredModel& discoveredModel = _nameHashToDiscoveredModel[request.placement.nameHash]; name.name = StringUtils::GetFileNameFromPath(discoveredModel.name); name.fullName = discoveredModel.name; name.nameHash = discoveredModel.nameHash; u32 modelID = _nameHashToModelID[request.placement.nameHash]; - u32 instanceID = _modelRenderer->AddPlacementInstance(entityID, modelID, request.placement); + u32 instanceID = _modelRenderer->AddPlacementInstance(entityID, modelID, nullptr, request.placement); - ECS::Components::Model& model = registry->get(entityID); + auto& model = registry->get(entityID); model.modelID = modelID; model.instanceID = instanceID; const ECS::Components::AABB& modelAABB = _modelIDToAABB[modelID]; - ECS::Components::AABB& aabb = registry->get(entityID); + auto& aabb = registry->get(entityID); aabb.centerPos = modelAABB.centerPos; aabb.extents = modelAABB.extents; @@ -738,7 +774,7 @@ void ModelLoader::AddStaticInstance(entt::entity entityID, const LoadRequestInte { i32 physicsEnabled = *CVarSystem::Get()->GetIntCVar(CVarCategory::Client | CVarCategory::Physics, "enabled"_h); - if (physicsEnabled && !hasParent) + if (physicsEnabled) { entt::registry* registry = ServiceLocator::GetEnttRegistries()->gameRegistry; auto& joltState = registry->ctx().get(); @@ -748,7 +784,7 @@ void ModelLoader::AddStaticInstance(entt::entity entityID, const LoadRequestInte // TODO: We need to scale the shape - ECS::Components::Transform& transform = registry->get(entityID); + auto& transform = registry->get(entityID); vec3 position = transform.GetWorldPosition(); const quat& rotation = transform.GetWorldRotation(); @@ -760,13 +796,16 @@ void ModelLoader::AddStaticInstance(entt::entity entityID, const LoadRequestInte if (body) { JPH::BodyID bodyID = body->GetID(); + + // Store the entity ID in the body so we can look it up later + body->SetUserData(static_cast(entityID)); + bodyInterface.AddBody(bodyID, JPH::EActivation::Activate); _instanceIDToBodyID[instanceID] = bodyID.GetIndexAndSequenceNumber(); } } } - /* Commented out on purpose to be dealt with at a later date when we have reimplemented GPU side animations */ Animation::AnimationSystem* animationSystem = ServiceLocator::GetAnimationSystem(); if (animationSystem->AddInstance(modelID, instanceID)) { @@ -779,34 +818,37 @@ void ModelLoader::AddStaticInstance(entt::entity entityID, const LoadRequestInte void ModelLoader::AddDynamicInstance(entt::entity entityID, const LoadRequestInternal& request) { + Animation::AnimationSystem* animationSystem = ServiceLocator::GetAnimationSystem(); entt::registry* registry = ServiceLocator::GetEnttRegistries()->gameRegistry; DiscoveredModel& discoveredModel = _nameHashToDiscoveredModel[request.placement.nameHash]; - ECS::Components::Name& name = registry->get(entityID); + auto& name = registry->get(entityID); name.name = StringUtils::GetFileNameFromPath(discoveredModel.name); name.fullName = discoveredModel.name; name.nameHash = discoveredModel.nameHash; - ECS::Components::Model& model = registry->get(entityID); + auto& model = registry->get(entityID); u32 modelID = _nameHashToModelID[request.placement.nameHash]; + Model::ComplexModel* complexModel = _modelIDToComplexModel[modelID]; u32 instanceID = model.instanceID; - ECS::Components::Transform& transform = registry->get(entityID); + auto& transform = registry->get(entityID); if (instanceID == std::numeric_limits().max()) { - instanceID = _modelRenderer->AddInstance(entityID, modelID, transform.GetMatrix()); + instanceID = _modelRenderer->AddInstance(entityID, modelID, complexModel, transform.GetMatrix(), request.displayID); } else { - _modelRenderer->ModifyInstance(entityID, instanceID, modelID, transform.GetMatrix()); + animationSystem->RemoveInstance(instanceID); + _modelRenderer->ModifyInstance(entityID, instanceID, modelID, complexModel, transform.GetMatrix(), request.displayID); } model.modelID = modelID; model.instanceID = instanceID; const ECS::Components::AABB& modelAABB = _modelIDToAABB[modelID]; - ECS::Components::AABB& aabb = registry->get(entityID); + auto& aabb = registry->get(entityID); aabb.centerPos = modelAABB.centerPos; aabb.extents = modelAABB.extents; @@ -814,7 +856,49 @@ void ModelLoader::AddDynamicInstance(entt::entity entityID, const LoadRequestInt _instanceIDToModelID[instanceID] = modelID; _instanceIDToEntityID[instanceID] = entityID; - Animation::AnimationSystem* animationSystem = ServiceLocator::GetAnimationSystem(); + if (discoveredModel.hasShape) + { + i32 physicsEnabled = *CVarSystem::Get()->GetIntCVar(CVarCategory::Client | CVarCategory::Physics, "enabled"_h); + + if (physicsEnabled && registry->all_of(entityID)) + { + entt::registry* registry = ServiceLocator::GetEnttRegistries()->gameRegistry; + auto& joltState = registry->ctx().get(); + JPH::BodyInterface& bodyInterface = joltState.physicsSystem.GetBodyInterface(); + + const JPH::ShapeRefC& shape = _nameHashToJoltShape[request.placement.nameHash]; + + // TODO: We need to scale the shape + + auto& transform = registry->get(entityID); + vec3 position = transform.GetWorldPosition(); + const quat& rotation = transform.GetWorldRotation(); + + // Create the settings for the body itself. Note that here you can also set other properties like the restitution / friction. + JPH::BodyCreationSettings bodySettings(new JPH::ScaledShapeSettings(shape, JPH::Vec3(2.0f, 1.0f, 0.5f)), JPH::RVec3(position.x, position.y, position.z), JPH::Quat(rotation.x, rotation.y, rotation.z, rotation.w), JPH::EMotionType::Kinematic, Jolt::Layers::NON_MOVING); + bodySettings.mAllowedDOFs = JPH::EAllowedDOFs::TranslationX | JPH::EAllowedDOFs::TranslationY | JPH::EAllowedDOFs::TranslationZ; + bodySettings.mOverrideMassProperties = JPH::EOverrideMassProperties::CalculateInertia; + bodySettings.mMassPropertiesOverride.mMass = 1000.0f; + bodySettings.mIsSensor = true; + + // Create the actual rigid body + JPH::Body* body = bodyInterface.CreateBody(bodySettings); // Note that if we run out of bodies this can return nullptr + if (body) + { + JPH::BodyID bodyID = body->GetID(); + + auto& networkedEntity = registry->get(entityID); + networkedEntity.bodyID = bodyID.GetIndexAndSequenceNumber(); + + // Store the entity ID in the body so we can look it up later + body->SetUserData(static_cast(entityID)); + + bodyInterface.AddBody(bodyID, JPH::EActivation::Activate); + _instanceIDToBodyID[instanceID] = bodyID.GetIndexAndSequenceNumber(); + } + } + } + if (animationSystem->AddInstance(modelID, instanceID)) { animationSystem->SetBoneSequence(instanceID, Animation::Bone::Default, Animation::Type::Stand, Animation::Flag::Loop); diff --git a/Source/Game/Game/Rendering/Model/ModelLoader.h b/Source/Game/Game/Rendering/Model/ModelLoader.h index f010b24c..40f1b5e4 100644 --- a/Source/Game/Game/Rendering/Model/ModelLoader.h +++ b/Source/Game/Game/Rendering/Model/ModelLoader.h @@ -41,12 +41,19 @@ class ModelLoader Model::ComplexModel::ModelHeader modelHeader; }; + struct UnloadRequest + { + entt::entity entity; + u32 instanceID; + }; + private: struct LoadRequestInternal { public: entt::entity entity; u32 instanceID = std::numeric_limits().max(); + u32 displayID = std::numeric_limits().max(); Terrain::Placement placement; }; @@ -63,8 +70,9 @@ class ModelLoader void LoadPlacement(const Terrain::Placement& placement); void LoadDecoration(u32 instanceID, const Model::ComplexModel::Decoration& decoration); void LoadModelForEntity(entt::entity entity, u32 modelNameHash); + bool LoadDisplayIDForEntity(entt::entity entity, u32 displayID = std::numeric_limits().max()); - void UnloadModelForEntity(entt::entity entity, u32 modelID); + void UnloadModelForEntity(entt::entity entity, u32 instanceID); u32 GetModelHashFromModelPath(const std::string& modelPath); bool GetModelIDFromInstanceID(u32 instanceID, u32& modelID); @@ -86,6 +94,8 @@ class ModelLoader std::vector _dynamicLoadRequests; moodycamel::ConcurrentQueue _dynamicRequests; + moodycamel::ConcurrentQueue _unloadRequests; + robin_hood::unordered_map _nameHashToLoadState; robin_hood::unordered_map _nameHashToModelID; robin_hood::unordered_map _nameHashToJoltShape; diff --git a/Source/Game/Game/Rendering/Model/ModelRenderer.cpp b/Source/Game/Game/Rendering/Model/ModelRenderer.cpp index b5d296af..f1d43883 100644 --- a/Source/Game/Game/Rendering/Model/ModelRenderer.cpp +++ b/Source/Game/Game/Rendering/Model/ModelRenderer.cpp @@ -10,6 +10,7 @@ #include "Game/Application/EnttRegistries.h" #include "Game/ECS/Components/Model.h" #include "Game/ECS/Components/Tags.h" +#include "Game/ECS/Singletons/ClientDBCollection.h" #include "Game/ECS/Singletons/TextureSingleton.h" #include "Game/ECS/Util/Transforms.h" @@ -952,7 +953,7 @@ u32 ModelRenderer::LoadModel(const std::string& name, Model::ComplexModel& model entt::registry* registry = registries->gameRegistry; entt::registry::context& ctx = registry->ctx(); - ECS::Singletons::TextureSingleton& textureSingleton = ctx.get(); + auto& textureSingleton = ctx.get(); // Add ModelManifest u32 modelManifestIndex = _modelManifestsIndex.fetch_add(1); @@ -1042,7 +1043,7 @@ u32 ModelRenderer::LoadModel(const std::string& name, Model::ComplexModel& model Model::ComplexModel::TextureUnit& cTextureUnit = renderBatch.textureUnits[i]; Model::ComplexModel::Material& cMaterial = model.materials[cTextureUnit.materialIndex]; - u16 materialFlag = *reinterpret_cast(&cMaterial.flags) << 1; + u16 materialFlag = *reinterpret_cast(&cMaterial.flags) << 5; u16 blendingMode = static_cast(cMaterial.blendingMode) << 11; textureUnit.data = static_cast(cTextureUnit.flags.IsProjectedTexture) | materialFlag | blendingMode; @@ -1077,15 +1078,9 @@ u32 ModelRenderer::LoadModel(const std::string& name, Model::ComplexModel& model { textureDesc.path = textureSingleton.textureHashToPath[cTexture.textureHash]; } - else if (cTexture.type == Model::ComplexModel::Texture::Type::Skin) + else { - static const u32 defaultSkinHash = "textures/bakednpctextures/creaturedisplayextra-00872.dds"_h; - textureDesc.path = textureSingleton.textureHashToPath[defaultSkinHash]; - } - else if (cTexture.type == Model::ComplexModel::Texture::Type::CharacterHair) - { - static const u32 defaultHairHash = "character/human/female/humanfemalehairlongwavy.dds"_h; - textureDesc.path = textureSingleton.textureHashToPath[defaultHairHash]; + continue; } if (textureDesc.path.size() > 0) @@ -1095,6 +1090,16 @@ u32 ModelRenderer::LoadModel(const std::string& name, Model::ComplexModel& model NC_ASSERT(textureUnit.textureIds[j] < Renderer::Settings::MAX_TEXTURES, "ModelRenderer : LoadModel overflowed the {0} textures we have support for", Renderer::Settings::MAX_TEXTURES); } + + u8 textureSamplerIndex = 0; + + if (cTexture.flags.wrapX) + textureSamplerIndex |= 0x1; + + if (cTexture.flags.wrapY) + textureSamplerIndex |= 0x2; + + textureUnit.data |= textureSamplerIndex << (1 + (j * 2)); } } @@ -1109,12 +1114,15 @@ u32 ModelRenderer::LoadModel(const std::string& name, Model::ComplexModel& model switch (renderBatch.groupID) { case 0: // Base - case 2: // Bald Head + case 1: // Bald Head + case 101: // Beard + case 201: // Sideburns + case 301: // Moustache case 401: // Gloves case 501: // Boots case 702: // Ears case 1301: // Legs - case 1501: // Upper Back + case 1501: // Cloak //case 1703: // DK Eye Glow (Needs further support to be animated) break; @@ -1194,7 +1202,7 @@ u32 ModelRenderer::LoadModel(const std::string& name, Model::ComplexModel& model return modelManifestIndex; } -u32 ModelRenderer::AddPlacementInstance(entt::entity entityID, u32 modelID, const Terrain::Placement& placement) +u32 ModelRenderer::AddPlacementInstance(entt::entity entityID, u32 modelID, Model::ComplexModel* model, const Terrain::Placement& placement) { vec3 scale = vec3(placement.scale) / 1024.0f; @@ -1203,7 +1211,7 @@ u32 ModelRenderer::AddPlacementInstance(entt::entity entityID, u32 modelID, cons mat4x4 scaleMatrix = glm::scale(mat4x4(1.0f), scale); mat4x4 instanceMatrix = glm::translate(mat4x4(1.0f), placement.position) * rotationMatrix * scaleMatrix; - u32 instanceID = AddInstance(entityID, modelID, instanceMatrix); + u32 instanceID = AddInstance(entityID, modelID, model, instanceMatrix); ModelManifest& manifest = _modelManifests[modelID]; @@ -1243,7 +1251,7 @@ u32 ModelRenderer::AddPlacementInstance(entt::entity entityID, u32 modelID, cons return instanceID; } -u32 ModelRenderer::AddInstance(entt::entity entityID, u32 modelID, const mat4x4& transformMatrix) +u32 ModelRenderer::AddInstance(entt::entity entityID, u32 modelID, Model::ComplexModel* model, const mat4x4& transformMatrix, u32 displayID) { entt::registry* registry = ServiceLocator::GetEnttRegistries()->gameRegistry; bool isSkybox = registry->all_of(entityID); @@ -1364,16 +1372,21 @@ u32 ModelRenderer::AddInstance(entt::entity entityID, u32 modelID, const mat4x4& } } + if (model && displayID != std::numeric_limits().max()) + { + ReplaceTextureUnits(modelID, model, instanceID, displayID); + } + return instanceID; } -void ModelRenderer::ModifyInstance(entt::entity entityID, u32 instanceID, u32 modelID, const mat4x4& transformMatrix) +void ModelRenderer::ModifyInstance(entt::entity entityID, u32 instanceID, u32 modelID, Model::ComplexModel* model, const mat4x4& transformMatrix, u32 displayID) { InstanceData& instanceData = _instanceDatas.Get()[instanceID]; u32 oldModelID = instanceData.modelID; - if (modelID == oldModelID) + if (modelID == oldModelID && displayID == std::numeric_limits().max()) return; entt::registry* registry = ServiceLocator::GetEnttRegistries()->gameRegistry; @@ -1562,6 +1575,391 @@ void ModelRenderer::ModifyInstance(entt::entity entityID, u32 instanceID, u32 mo transparentCullingResources.GetDrawCallDatas().SetDirtyElements(oldTransparentBaseIndex, oldTransparentNumDrawCalls); } } + + if (model && displayID != std::numeric_limits().max()) + { + ReplaceTextureUnits(modelID, model, instanceID, displayID); + } +} + +void ModelRenderer::ReplaceTextureUnits(u32 modelID, Model::ComplexModel* model, u32 instanceID, u32 displayID) +{ + entt::registry* registry = ServiceLocator::GetEnttRegistries()->gameRegistry; + auto& clientDBCollection = registry->ctx().get(); + auto& textureSingleton = registry->ctx().get(); + auto* creatureDisplayStorage = clientDBCollection.Get(ECS::Singletons::ClientDBHash::CreatureDisplayInfo); + + ClientDB::Definitions::CreatureDisplayInfo* creatureDisplayInfo = nullptr; + ClientDB::Definitions::CreatureDisplayInfoExtra* creatureDisplayInfoExtra = nullptr; + if (creatureDisplayStorage) + { + creatureDisplayInfo = creatureDisplayStorage->GetRow(displayID); + + if (creatureDisplayInfo->extendedDisplayInfoID != 0) + { + if (auto* creatureDisplayInfoExtraStorage = clientDBCollection.Get(ECS::Singletons::ClientDBHash::CreatureDisplayInfoExtra)) + { + creatureDisplayInfoExtra = creatureDisplayInfoExtraStorage->GetRow(creatureDisplayInfo->extendedDisplayInfoID); + } + } + } + + ModelManifest& manifest = _modelManifests[modelID]; + + bool hasDynamicTextureUnits = false; + + u32 numTextureUnits = 0; + for (auto& renderBatch : model->modelData.renderBatches) + { + numTextureUnits += static_cast(renderBatch.textureUnits.size()); + + if (hasDynamicTextureUnits) + continue; + + for (u32 i = 0; i < renderBatch.textureUnits.size(); i++) + { + Model::ComplexModel::TextureUnit& cTextureUnit = renderBatch.textureUnits[i]; + + for (u32 j = 0; j < cTextureUnit.textureCount && j < 2; j++) + { + u16 textureIndex = model->textureIndexLookupTable[cTextureUnit.textureIndexStart + j]; + if (textureIndex == 65535) + continue; + + Model::ComplexModel::Texture& cTexture = model->textures[textureIndex]; + + switch (cTexture.type) + { + case Model::ComplexModel::Texture::Type::Skin: + case Model::ComplexModel::Texture::Type::ObjectSkin: + case Model::ComplexModel::Texture::Type::CharacterHair: + case Model::ComplexModel::Texture::Type::CharacterFacialHair: + case Model::ComplexModel::Texture::Type::SkinExtra: + case Model::ComplexModel::Texture::Type::MonsterSkin1: + case Model::ComplexModel::Texture::Type::MonsterSkin2: + case Model::ComplexModel::Texture::Type::MonsterSkin3: + { + hasDynamicTextureUnits = true; + break; + } + + default: break; + } + + if (hasDynamicTextureUnits) + break; + } + + if (hasDynamicTextureUnits) + break; + } + } + + if (!hasDynamicTextureUnits) + return; + + u32 numRenderBatches = static_cast(model->modelData.renderBatches.size()); + u32 textureTransformLookupTableSize = static_cast(model->textureTransformLookupTable.size()); + + u32 opaqueDrawCallOffset = _instanceIDToOpaqueDrawCallOffset[instanceID]; + u32 transparentDrawCallOffset = _instanceIDToTransparentDrawCallOffset[instanceID]; + + u32 numIteratedOpaqueDrawCalls = 0; + u32 numIteratedTransparentDrawCalls = 0; + + // Get the correct culling resources + CullingResourcesIndexed& opaqueCullingResources = _opaqueCullingResources; + + std::vector& opaqueDrawCalls = opaqueCullingResources.GetDrawCalls().Get(); + std::vector& opaqueDrawCallDatas = opaqueCullingResources.GetDrawCallDatas().Get(); + + CullingResourcesIndexed& transparentCullingResources = _transparentCullingResources; + + std::vector& transparentDrawCalls = transparentCullingResources.GetDrawCalls().Get(); + std::vector& transparentDrawCallDatas = transparentCullingResources.GetDrawCallDatas().Get(); + + for (u32 renderBatchIndex = 0; renderBatchIndex < numRenderBatches; renderBatchIndex++) + { + Model::ComplexModel::RenderBatch& renderBatch = model->modelData.renderBatches[renderBatchIndex]; + + u32 textureUnitBaseIndex = _textureUnitIndex.fetch_add(static_cast(renderBatch.textureUnits.size())); + u16 numUnlitTextureUnits = 0; + + for (u32 i = 0; i < renderBatch.textureUnits.size(); i++) + { + // Texture Unit + TextureUnit& textureUnit = _textureUnits.Get()[textureUnitBaseIndex + i]; + + Model::ComplexModel::TextureUnit& cTextureUnit = renderBatch.textureUnits[i]; + Model::ComplexModel::Material& cMaterial = model->materials[cTextureUnit.materialIndex]; + + u16 materialFlag = *reinterpret_cast(&cMaterial.flags) << 5; + u16 blendingMode = static_cast(cMaterial.blendingMode) << 11; + + textureUnit.data = static_cast(cTextureUnit.flags.IsProjectedTexture) | materialFlag | blendingMode; + textureUnit.materialType = cTextureUnit.shaderID; + + u16 textureTransformID1 = MODEL_INVALID_TEXTURE_TRANSFORM_ID; + if (cTextureUnit.textureTransformIndexStart < textureTransformLookupTableSize) + textureTransformID1 = model->textureTransformLookupTable[cTextureUnit.textureTransformIndexStart]; + + u16 textureTransformID2 = MODEL_INVALID_TEXTURE_TRANSFORM_ID; + if (cTextureUnit.textureCount > 1) + if (cTextureUnit.textureTransformIndexStart + 1u < textureTransformLookupTableSize) + textureTransformID2 = model->textureTransformLookupTable[cTextureUnit.textureTransformIndexStart + 1]; + + textureUnit.textureTransformIds[0] = textureTransformID1; + textureUnit.textureTransformIds[1] = textureTransformID2; + + numUnlitTextureUnits += (materialFlag & 0x2) > 0; + + // Textures + for (u32 j = 0; j < cTextureUnit.textureCount && j < 2; j++) + { + std::scoped_lock lock(_textureLoadMutex); + + Renderer::TextureDesc textureDesc; + u16 textureIndex = model->textureIndexLookupTable[cTextureUnit.textureIndexStart + j]; + if (textureIndex == 65535) + continue; + + Model::ComplexModel::Texture& cTexture = model->textures[textureIndex]; + u32 textureHash = cTexture.textureHash; + + static auto GetRaceSkinTextureForDisplayID = [](u32 displayID) -> u32 + { + switch (displayID) + { + case 49: // Human Male + { + return "textures/bakednpctextures/creaturedisplayextra-00030.dds"_h; + } + case 50: // Human Female + { + return "textures/bakednpctextures/creaturedisplayextra-154629.dds"_h; + } + case 51: // Orc Male + { + return "textures/bakednpctextures/creaturedisplayextra-00111.dds"_h; + } + case 52: // Orc Female + { + return "textures/bakednpctextures/creaturedisplayextra-00102.dds"_h; + } + case 53: // Dwarf Male + { + return "textures/bakednpctextures/creaturedisplayextra-156637.dds"_h; + } + case 54: // Dwarf Female + { + return "textures/bakednpctextures/creaturedisplayextra-156636.dds"_h; + } + case 55: // Night Elf Male + { + return "textures/bakednpctextures/creaturedisplayextra-00405.dds"_h; + } + case 56: // Night Elf Female + { + return "textures/bakednpctextures/creaturedisplayextra-00404.dds"_h; + } + case 57: // Undead Male + { + return "textures/bakednpctextures/creaturedisplayextra-00046.dds"_h; + } + case 58: // Undead Female + { + return "textures/bakednpctextures/creaturedisplayextra-00262.dds"_h; + } + case 59: // Tauren Male + { + return "textures/bakednpctextures/creaturedisplayextra-00382.dds"_h; + } + case 60: // Tauren Female + { + return "textures/bakednpctextures/creaturedisplayextra-02034.dds"_h; + } + case 1563: // Gnome Male + { + return "textures/bakednpctextures/creaturedisplayextra-01202.dds"_h; + } + case 1564: // Gnome Female + { + return "textures/bakednpctextures/creaturedisplayextra-01822.dds"_h; + } + case 1478: // Troll Male + { + return "textures/bakednpctextures/creaturedisplayextra-02639.dds"_h; + } + case 1479: // Troll Female + { + return "textures/bakednpctextures/creaturedisplayextra-01118.dds"_h; + } + + default: return "textures/bakednpctextures/creaturedisplayextra-00030.dds"_h; + } + }; + static auto GetRaceHairTextureForDisplayID = [](u32 displayID) -> u32 + { + switch (displayID) + { + case 49: // Human Male + { + return "character/human/hair00_01.dds"_h; + } + case 50: // Human Female + { + return "character/human/hair00_01.dds"_h; + } + case 51: // Orc Male + { + return "character/orc/hair00_00.dds"_h; + } + case 52: // Orc Female + { + return "character/orc/hair00_00.dds"_h; + } + case 53: // Dwarf Male + { + return "character/dwarf/hair00_05.dds"_h; + } + case 54: // Dwarf Female + { + return "character/dwarf/hair00_05.dds"_h; + } + case 55: // Night Elf Male + { + return "character/nightelf/hair00_06.dds"_h; + } + case 56: // Night Elf Female + { + return "character/nightelf/hair00_06.dds"_h; + } + case 57: // Undead Male + { + return "character/scourge/hair00_05.dds"_h; + } + case 58: // Undead Female + { + return "character/scourge/hair00_05.dds"_h; + } + case 59: // Tauren Male + { + return "character/tauren/scalplowerhair00_00.dds"_h; + } + case 60: // Tauren Female + { + return "character/tauren/scalplowerhair00_00.dds"_h; + } + case 1563: // Gnome Male + { + return "character/gnome/hair00_00.dds"_h; + } + case 1564: // Gnome Female + { + return "character/gnome/hair00_00.dds"_h; + } + case 1478: // Troll Male + { + return "character/troll/hair00_07.dds"_h; + } + case 1479: // Troll Female + { + return "character/troll/hair00_07.dds"_h; + } + + default: return "character/human/hair00_01.dds"_h; + } + }; + + if (cTexture.type == Model::ComplexModel::Texture::Type::None) + { + textureDesc.path = textureSingleton.textureHashToPath[cTexture.textureHash]; + } + else if (cTexture.type == Model::ComplexModel::Texture::Type::Skin) + { + u32 skinHash = cTexture.textureHash; + if (creatureDisplayInfoExtra) + { + skinHash = creatureDisplayInfoExtra->bakedTextureHash; + } + else + { + skinHash = GetRaceSkinTextureForDisplayID(displayID); + } + + textureDesc.path = textureSingleton.textureHashToPath[skinHash]; + } + else if (cTexture.type == Model::ComplexModel::Texture::Type::CharacterHair) + { + u32 defaultHairHash = GetRaceHairTextureForDisplayID(displayID); + textureDesc.path = textureSingleton.textureHashToPath[defaultHairHash]; + } + else if (cTexture.type == Model::ComplexModel::Texture::Type::MonsterSkin1) + { + if (creatureDisplayInfo) + { + textureHash = creatureDisplayInfo->textureVariations[0]; + textureDesc.path = textureSingleton.textureHashToPath[textureHash]; + } + } + else if (cTexture.type == Model::ComplexModel::Texture::Type::MonsterSkin2) + { + if (creatureDisplayInfo) + { + textureHash = creatureDisplayInfo->textureVariations[1]; + textureDesc.path = textureSingleton.textureHashToPath[textureHash]; + } + } + else if (cTexture.type == Model::ComplexModel::Texture::Type::MonsterSkin3) + { + if (creatureDisplayInfo) + { + textureHash = creatureDisplayInfo->textureVariations[2]; + textureDesc.path = textureSingleton.textureHashToPath[textureHash]; + } + } + + if (textureDesc.path.size() > 0) + { + Renderer::TextureID textureID = _renderer->LoadTextureIntoArray(textureDesc, _textures, textureUnit.textureIds[j]); + textureSingleton.textureHashToTextureID[textureHash] = static_cast(textureID); + + NC_ASSERT(textureUnit.textureIds[j] < Renderer::Settings::MAX_TEXTURES, "ModelRenderer : ReplaceTextureUnits overflowed the {0} textures we have support for", Renderer::Settings::MAX_TEXTURES); + } + + u8 textureSamplerIndex = 0; + + if (cTexture.flags.wrapX) + textureSamplerIndex |= 0x1; + + if (cTexture.flags.wrapY) + textureSamplerIndex |= 0x2; + + textureUnit.data |= textureSamplerIndex << (1 + (j * 2)); + } + } + + u32& numHandledDrawCalls = (renderBatch.isTransparent) ? numIteratedTransparentDrawCalls : numIteratedOpaqueDrawCalls; + u32& drawCallOffset = (renderBatch.isTransparent) ? transparentDrawCallOffset : opaqueDrawCallOffset; + + u32 curDrawCallOffset = drawCallOffset + numHandledDrawCalls; + + DrawCallData& drawCallData = (renderBatch.isTransparent) ? transparentDrawCallDatas[curDrawCallOffset] : opaqueDrawCallDatas[curDrawCallOffset]; + drawCallData.textureUnitOffset = textureUnitBaseIndex; + drawCallData.numTextureUnits = static_cast(renderBatch.textureUnits.size()); + drawCallData.numUnlitTextureUnits = numUnlitTextureUnits; + + if (renderBatch.isTransparent) + { + transparentCullingResources.GetDrawCallDatas().SetDirtyElement(curDrawCallOffset); + } + else + { + opaqueCullingResources.GetDrawCallDatas().SetDirtyElement(curDrawCallOffset); + } + + numHandledDrawCalls++; + } } bool ModelRenderer::AddAnimationInstance(u32 instanceID) @@ -1695,19 +2093,73 @@ void ModelRenderer::CreatePermanentResources() _renderer->LoadTextureIntoArray(debugTextureDesc, _textures, arrayIndex); - Renderer::SamplerDesc samplerDesc; - samplerDesc.enabled = true; - samplerDesc.filter = Renderer::SamplerFilter::MIN_MAG_MIP_LINEAR; - samplerDesc.addressU = Renderer::TextureAddressMode::WRAP; - samplerDesc.addressV = Renderer::TextureAddressMode::WRAP; - samplerDesc.addressW = Renderer::TextureAddressMode::CLAMP; - samplerDesc.shaderVisibility = Renderer::ShaderVisibility::PIXEL; - - _sampler = _renderer->CreateSampler(samplerDesc); - _opaqueCullingResources.GetGeometryPassDescriptorSet().Bind("_sampler"_h, _sampler); - _transparentCullingResources.GetGeometryPassDescriptorSet().Bind("_sampler"_h, _sampler); - _opaqueSkyboxCullingResources.GetGeometryPassDescriptorSet().Bind("_sampler"_h, _sampler); - _transparentSkyboxCullingResources.GetGeometryPassDescriptorSet().Bind("_sampler"_h, _sampler); + static constexpr u32 NumSamplers = 4; + _samplers.reserve(NumSamplers); + + // Sampler Clamp, Clamp + { + Renderer::SamplerDesc samplerDesc; + samplerDesc.enabled = true; + samplerDesc.filter = Renderer::SamplerFilter::MIN_MAG_MIP_LINEAR; + samplerDesc.addressU = Renderer::TextureAddressMode::CLAMP; + samplerDesc.addressV = Renderer::TextureAddressMode::CLAMP; + samplerDesc.addressW = Renderer::TextureAddressMode::CLAMP; + samplerDesc.shaderVisibility = Renderer::ShaderVisibility::PIXEL; + + Renderer::SamplerID samplerID = _renderer->CreateSampler(samplerDesc); + _samplers.push_back(samplerID); + } + + // Sampler Wrap, Clamp + { + Renderer::SamplerDesc samplerDesc; + samplerDesc.enabled = true; + samplerDesc.filter = Renderer::SamplerFilter::MIN_MAG_MIP_LINEAR; + samplerDesc.addressU = Renderer::TextureAddressMode::WRAP; + samplerDesc.addressV = Renderer::TextureAddressMode::CLAMP; + samplerDesc.addressW = Renderer::TextureAddressMode::CLAMP; + samplerDesc.shaderVisibility = Renderer::ShaderVisibility::PIXEL; + + Renderer::SamplerID samplerID = _renderer->CreateSampler(samplerDesc); + _samplers.push_back(samplerID); + } + + // Sampler Clamp, Wrap + { + Renderer::SamplerDesc samplerDesc; + samplerDesc.enabled = true; + samplerDesc.filter = Renderer::SamplerFilter::MIN_MAG_MIP_LINEAR; + samplerDesc.addressU = Renderer::TextureAddressMode::CLAMP; + samplerDesc.addressV = Renderer::TextureAddressMode::WRAP; + samplerDesc.addressW = Renderer::TextureAddressMode::CLAMP; + samplerDesc.shaderVisibility = Renderer::ShaderVisibility::PIXEL; + + Renderer::SamplerID samplerID = _renderer->CreateSampler(samplerDesc); + _samplers.push_back(samplerID); + } + + // Sampler Wrap, Wrap + { + Renderer::SamplerDesc samplerDesc; + samplerDesc.enabled = true; + samplerDesc.filter = Renderer::SamplerFilter::MIN_MAG_MIP_LINEAR; + samplerDesc.addressU = Renderer::TextureAddressMode::WRAP; + samplerDesc.addressV = Renderer::TextureAddressMode::WRAP; + samplerDesc.addressW = Renderer::TextureAddressMode::CLAMP; + samplerDesc.shaderVisibility = Renderer::ShaderVisibility::PIXEL; + + Renderer::SamplerID samplerID = _renderer->CreateSampler(samplerDesc); + _samplers.push_back(samplerID); + } + + for (u32 i = 0; i < NumSamplers; i++) + { + _opaqueCullingResources.GetGeometryPassDescriptorSet().BindArray("_samplers"_h, _samplers[i], i); + _transparentCullingResources.GetGeometryPassDescriptorSet().BindArray("_samplers"_h, _samplers[i], i); + _opaqueSkyboxCullingResources.GetGeometryPassDescriptorSet().BindArray("_samplers"_h, _samplers[i], i); + _transparentSkyboxCullingResources.GetGeometryPassDescriptorSet().BindArray("_samplers"_h, _samplers[i], i); + _materialPassDescriptorSet.BindArray("_samplers"_h, _samplers[i], i); + } CullingResourcesIndexed::InitParams initParams; initParams.renderer = _renderer; diff --git a/Source/Game/Game/Rendering/Model/ModelRenderer.h b/Source/Game/Game/Rendering/Model/ModelRenderer.h index 48be6424..ae5b9bf8 100644 --- a/Source/Game/Game/Rendering/Model/ModelRenderer.h +++ b/Source/Game/Game/Rendering/Model/ModelRenderer.h @@ -144,9 +144,10 @@ class ModelRenderer : CulledRenderer void Reserve(const ReserveInfo& reserveInfo); void FitBuffersAfterLoad(); u32 LoadModel(const std::string& name, Model::ComplexModel& model); - u32 AddPlacementInstance(entt::entity entityID, u32 modelID, const Terrain::Placement& placement); - u32 AddInstance(entt::entity entityID, u32 modelID, const mat4x4& transformMatrix); - void ModifyInstance(entt::entity entityID, u32 instanceID, u32 modelID, const mat4x4& transformMatrix); + u32 AddPlacementInstance(entt::entity entityID, u32 modelID, Model::ComplexModel* model, const Terrain::Placement& placement); + u32 AddInstance(entt::entity entityID, u32 modelID, Model::ComplexModel* model, const mat4x4& transformMatrix, u32 displayID = std::numeric_limits().max()); + void ModifyInstance(entt::entity entityID, u32 instanceID, u32 modelID, Model::ComplexModel* model, const mat4x4& transformMatrix, u32 displayID = std::numeric_limits().max()); + void ReplaceTextureUnits(u32 modelID, Model::ComplexModel* model, u32 instanceID, u32 displayID); bool AddAnimationInstance(u32 instanceID); bool SetBoneMatricesAsDirty(u32 instanceID, u32 localBoneIndex, u32 count, mat4x4* boneMatrixArray); @@ -257,7 +258,7 @@ class ModelRenderer : CulledRenderer Renderer::TextureArrayID _textures; - Renderer::SamplerID _sampler; + std::vector _samplers; Renderer::SamplerID _occlusionSampler; Renderer::DescriptorSet _materialPassDescriptorSet; diff --git a/Source/Game/Game/Rendering/Terrain/TerrainLoader.cpp b/Source/Game/Game/Rendering/Terrain/TerrainLoader.cpp index ef17d9f7..2666301c 100644 --- a/Source/Game/Game/Rendering/Terrain/TerrainLoader.cpp +++ b/Source/Game/Game/Rendering/Terrain/TerrainLoader.cpp @@ -328,11 +328,17 @@ void TerrainLoader::LoadFullMapRequest(const LoadRequestInternal& request) // Create the actual rigid body JPH::Body* body = bodyInterface.CreateBody(bodySettings); // Note that if we run out of bodies this can return nullptr - body->SetFriction(0.8f); - JPH::BodyID bodyID = body->GetID(); - bodyInterface.AddBody(bodyID, JPH::EActivation::Activate); - _chunkIDToBodyID[chunkID] = bodyID.GetIndexAndSequenceNumber(); + if (body) + { + body->SetUserData(std::numeric_limits().max()); + body->SetFriction(0.8f); + + JPH::BodyID bodyID = body->GetID(); + + bodyInterface.AddBody(bodyID, JPH::EActivation::Activate); + _chunkIDToBodyID[chunkID] = bodyID.GetIndexAndSequenceNumber(); + } } // Load into Terrain Renderer diff --git a/Source/Game/Game/Rendering/Terrain/TerrainRenderer.cpp b/Source/Game/Game/Rendering/Terrain/TerrainRenderer.cpp index c4d55d95..78f8d2a6 100644 --- a/Source/Game/Game/Rendering/Terrain/TerrainRenderer.cpp +++ b/Source/Game/Game/Rendering/Terrain/TerrainRenderer.cpp @@ -515,7 +515,7 @@ u32 TerrainRenderer::AddChunk(u32 chunkHash, Map::Chunk* chunk, ivec2 chunkGridP entt::registry* registry = registries->gameRegistry; entt::registry::context& ctx = registry->ctx(); - ECS::Singletons::TextureSingleton& textureSingleton = ctx.get(); + auto& textureSingleton = ctx.get(); ChunkData& chunkData = _chunkDatas.Get()[currentChunkIndex]; diff --git a/Source/Game/Game/Scripting/LuaManager.cpp b/Source/Game/Game/Scripting/LuaManager.cpp index bd7af566..12ba32ee 100644 --- a/Source/Game/Game/Scripting/LuaManager.cpp +++ b/Source/Game/Game/Scripting/LuaManager.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -25,7 +26,7 @@ AutoCVar_String CVAR_ScriptMotd(CVarCategory::Client, "scriptingMotd", "defines namespace Scripting { - LuaManager::LuaManager() : _state(nullptr) + LuaManager::LuaManager() : _internalState(nullptr), _publicState(nullptr) { _luaHandlers.reserve(16); _luaSystems.reserve(16); @@ -41,13 +42,13 @@ namespace Scripting if (LoadScripts()) { - RegisterLuaSystem(new GenericSystem(2)); + RegisterLuaSystem(new GenericSystem()); LuaGameEventLoadedData eventData; eventData.motd = CVAR_ScriptMotd.Get(); auto gameEventHandler = GetLuaHandler(LuaHandlerType::GameEvent); - gameEventHandler->CallEvent(_state, static_cast(LuaGameEvent::Loaded), &eventData); + gameEventHandler->CallEvent(_internalState, static_cast(LuaGameEvent::Loaded), &eventData); } } @@ -66,7 +67,7 @@ namespace Scripting eventData.motd = CVAR_ScriptMotd.Get(); auto gameEventHandler = GetLuaHandler(LuaHandlerType::GameEvent); - gameEventHandler->CallEvent(_state, static_cast(LuaGameEvent::Loaded), &eventData); + gameEventHandler->CallEvent(_internalState, static_cast(LuaGameEvent::Loaded), &eventData); } isDirty = result; @@ -78,43 +79,28 @@ namespace Scripting { LuaSystemBase* luaSystem = _luaSystems[i]; - u32 numStates = static_cast(luaSystem->_states.size()); - if (numStates == 0) - continue; - if (isDirty) { luaSystem->PushEvent(LuaSystemEvent::Reload); } - luaSystem->Update(deltaTime); - luaSystem->Prepare(deltaTime); + luaSystem->Prepare(deltaTime, _internalState); + luaSystem->Update(deltaTime, _internalState); scheduler->WaitforAll(); - for (u32 j = 0; j < numStates; j++) + u32 numTasks = static_cast(_tasks.size()); + + for (enki::TaskSet* task : _tasks) { - if (_tasks.size() <= j) - { - _tasks.push_back(new enki::TaskSet(nullptr)); - } - - enki::TaskSet* task = task = _tasks[j]; - task->m_Function = [&luaSystem, deltaTime, j](enki::TaskSetPartition range, uint32_t threadNum) - { - luaSystem->Run(deltaTime, j); - }; - - scheduler->AddTaskSetToPipe(task); + luaSystem->Run(deltaTime, _internalState); } } - - scheduler->WaitforAll(); } bool LuaManager::DoString(const std::string& code) { - LuaStateCtx ctx(_state); + LuaStateCtx ctx(_internalState); Luau::CompileOptions compileOptions; { @@ -132,14 +118,10 @@ namespace Scripting if (result != LUA_OK) { ctx.ReportError(); + return false; } - result = ctx.Resume(); - if (result != LUA_OK) - { - ctx.ReportError(); - } - + result = ctx.PCall(0, 0); return result == LUA_OK; } @@ -264,15 +246,15 @@ namespace Scripting if (!didFail) { - if (_state != nullptr) + if (_internalState != nullptr) { - gameEventHandler->ClearEvents(_state); + gameEventHandler->ClearEvents(_internalState); - LuaStateCtx oldCtx(_state); + LuaStateCtx oldCtx(_internalState); oldCtx.Close(); } - _state = ctx.GetState(); + _internalState = ctx.GetState(); } else { diff --git a/Source/Game/Game/Scripting/LuaManager.h b/Source/Game/Game/Scripting/LuaManager.h index 3edf3706..04bed1b8 100644 --- a/Source/Game/Game/Scripting/LuaManager.h +++ b/Source/Game/Game/Scripting/LuaManager.h @@ -72,7 +72,8 @@ namespace Scripting const LuaTable& GetGlobalTable() { return _globalTable; } private: - lua_State* _state; + lua_State* _internalState; + lua_State* _publicState; std::vector _luaHandlers; std::vector _luaSystems; diff --git a/Source/Game/Game/Scripting/LuaStateCtx.cpp b/Source/Game/Game/Scripting/LuaStateCtx.cpp index 35294682..b59831cc 100644 --- a/Source/Game/Game/Scripting/LuaStateCtx.cpp +++ b/Source/Game/Game/Scripting/LuaStateCtx.cpp @@ -73,52 +73,53 @@ namespace Scripting const std::any& value = pair.second; auto& type = value.type(); - if (value.type() == typeid(bool)) + + if (type == typeid(bool)) { auto val = std::any_cast(value); SetGlobal(key.c_str(), val); } - else if (value.type() == typeid(i32)) + else if (type == typeid(i32)) { auto val = std::any_cast(value); SetGlobal(key.c_str(), val); } - else if (value.type() == typeid(u32)) + else if (type == typeid(u32)) { auto val = std::any_cast(value); SetGlobal(key.c_str(), val); } - else if (value.type() == typeid(f32)) + else if (type == typeid(f32)) { auto val = std::any_cast(value); SetGlobal(key.c_str(), val); } - else if (value.type() == typeid(f64)) + else if (type == typeid(f64)) { auto val = std::any_cast(value); SetGlobal(key.c_str(), val); } - else if (value.type() == typeid(vec3)) + else if (type == typeid(vec3)) { auto val = std::any_cast(value); SetGlobal(key.c_str(), val); } - else if (value.type() == typeid(char*)) + else if (type == typeid(char*)) { auto val = std::any_cast(value); SetGlobal(key.c_str(), val); } - else if (value.type() == typeid(std::string)) + else if (type == typeid(std::string)) { auto val = std::any_cast(value); SetGlobal(key.c_str(), val.c_str()); } - else if (value.type() == typeid(lua_CFunction)) + else if (type == typeid(lua_CFunction)) { auto val = std::any_cast(value); SetGlobal(key.c_str(), val); } - else if (value.type() == typeid(LuaTable)) + else if (type == typeid(LuaTable)) { const auto& val = std::any_cast(value); SetGlobal(key.c_str(), val); @@ -145,7 +146,7 @@ namespace Scripting return lua_status(_state); } - void LuaStateCtx::PCall(i32 numResults /*= 0*/, i32 errorfunc /*= 0*/) + bool LuaStateCtx::PCall(i32 numResults /*= 0*/, i32 errorfunc /*= 0*/) { u32 numArgs = _pushCounter; @@ -158,6 +159,7 @@ namespace Scripting } _pushCounter = 0; + return result == LUA_OK; } void LuaStateCtx::PushNil(bool incrementPushCounter /*= true*/) @@ -358,7 +360,7 @@ namespace Scripting return luau_load(_state, chunkName.c_str(), bytecode.c_str(), bytecode.size(), env); } - i32 LuaStateCtx::Resume(i32 index, lua_State* from) + i32 LuaStateCtx::Resume(lua_State* from, i32 index) { return lua_resume(_state, from, index); } diff --git a/Source/Game/Game/Scripting/LuaStateCtx.h b/Source/Game/Game/Scripting/LuaStateCtx.h index 7a1b524d..f4ba88e8 100644 --- a/Source/Game/Game/Scripting/LuaStateCtx.h +++ b/Source/Game/Game/Scripting/LuaStateCtx.h @@ -31,7 +31,7 @@ namespace Scripting i32 GetStatus(); - void PCall(i32 numResults = 0, i32 errorfunc = 0); + bool PCall(i32 numResults = 0, i32 errorfunc = 0); void PushNil(bool incrementPushCounter = true); void PushBool(bool value, bool incrementPushCounter = true); @@ -90,7 +90,7 @@ namespace Scripting void SetTable(const char* key, LuaTable& value, i32 index = -3); i32 LoadBytecode(const std::string& chunkName, const std::string& bytecode, i32 env = 0); - i32 Resume(i32 index = 0, lua_State* from = nullptr); + i32 Resume(lua_State* from = nullptr, i32 index = 0); void MakeReadOnly(); void ReportError(); void Close(); diff --git a/Source/Game/Game/Scripting/Systems/GenericSystem.cpp b/Source/Game/Game/Scripting/Systems/GenericSystem.cpp index fb10679e..c67fb10e 100644 --- a/Source/Game/Game/Scripting/Systems/GenericSystem.cpp +++ b/Source/Game/Game/Scripting/Systems/GenericSystem.cpp @@ -9,21 +9,21 @@ namespace Scripting { - GenericSystem::GenericSystem(u32 numStates) : LuaSystemBase(numStates) { } + GenericSystem::GenericSystem() : LuaSystemBase() { } - void GenericSystem::Prepare(f32 deltaTime) + void GenericSystem::Prepare(f32 deltaTime, lua_State* state) { LuaManager* luaManager = ServiceLocator::GetLuaManager(); auto gameEventHandler = luaManager->GetLuaHandler(LuaHandlerType::GameEvent); LuaGameEventUpdatedData eventData; eventData.deltaTime = deltaTime; - gameEventHandler->CallEvent(_states[0], static_cast(LuaGameEvent::Updated), &eventData); + gameEventHandler->CallEvent(state, static_cast(LuaGameEvent::Updated), &eventData); } - void GenericSystem::Run(f32 deltaTime, u32 index) + void GenericSystem::Run(f32 deltaTime, lua_State* state) { - LuaStateCtx ctx(_states[index]); + LuaStateCtx ctx(state); // TODO :: Do Stuff } diff --git a/Source/Game/Game/Scripting/Systems/GenericSystem.h b/Source/Game/Game/Scripting/Systems/GenericSystem.h index b44cdf92..299c73fd 100644 --- a/Source/Game/Game/Scripting/Systems/GenericSystem.h +++ b/Source/Game/Game/Scripting/Systems/GenericSystem.h @@ -8,9 +8,9 @@ namespace Scripting class GenericSystem : public LuaSystemBase { public: - GenericSystem(u32 numStates); + GenericSystem(); - void Prepare(f32 deltaTime); - void Run(f32 deltaTime, u32 index); + void Prepare(f32 deltaTime, lua_State* state); + void Run(f32 deltaTime, lua_State* state); }; } \ No newline at end of file diff --git a/Source/Game/Game/Scripting/Systems/LuaSystemBase.cpp b/Source/Game/Game/Scripting/Systems/LuaSystemBase.cpp index 15586f9b..e48f9b44 100644 --- a/Source/Game/Game/Scripting/Systems/LuaSystemBase.cpp +++ b/Source/Game/Game/Scripting/Systems/LuaSystemBase.cpp @@ -13,30 +13,15 @@ namespace Scripting { - LuaSystemBase::LuaSystemBase(u32 numStates) : _events() + LuaSystemBase::LuaSystemBase() : _events() { - Init(numStates); } - void LuaSystemBase::Init(u32 numStates) + void LuaSystemBase::Init() { - u32 numCurrentStates = static_cast(_states.size()); - - for (u32 i = 0; i < numCurrentStates; i++) - { - DestroyState(i); - } - - _states.clear(); - _states.reserve(numStates); - - for (u32 i = 0; i < numStates; i++) - { - CreateState(); - } } - void LuaSystemBase::Update(f32 deltaTime) + void LuaSystemBase::Update(f32 deltaTime, lua_State* state) { LuaSystemEvent systemEvent; while (_events.try_dequeue(systemEvent)) @@ -45,9 +30,6 @@ namespace Scripting { case LuaSystemEvent::Reload: { - u32 numStates = static_cast(_states.size()); - Init(numStates); - break; } @@ -61,71 +43,71 @@ namespace Scripting _events.enqueue(systemEvent); } - lua_State* LuaSystemBase::CreateState() - { - LuaManager* luaManager = ServiceLocator::GetLuaManager(); - - LuaStateCtx ctx(luaL_newstate()); - - ctx.RegisterDefaultLibraries(); - - // Set Globals - { - const LuaTable& table = luaManager->GetGlobalTable(); - ctx.SetGlobal(table); - } - - ctx.MakeReadOnly(); - - const std::vector& bytecodeList = luaManager->GetBytecodeList(); - for (u32 j = 0; j < bytecodeList.size(); j++) - { - const LuaBytecodeEntry& bytecodeEntry = bytecodeList[j]; - std::string filePath = bytecodeEntry.filePath + "/" + bytecodeEntry.fileName; - - i32 result = ctx.LoadBytecode(filePath.c_str(), bytecodeEntry.bytecode, 0); - if (result != LUA_OK) - { - ctx.Pop(); - } - } - - auto gameEventHandler = luaManager->GetLuaHandler(LuaHandlerType::GameEvent); - - lua_State* state = ctx.GetState(); - gameEventHandler->SetupEvents(state); - - i32 top = ctx.GetTop(); - for (i32 i = 0; i < top; i++) - { - ctx.Resume(); - - i32 result = ctx.GetStatus(); - if (result != LUA_OK) - { - ctx.ReportError(); - break; - } - } - - _states.push_back(state); - return state; - } - - bool LuaSystemBase::DestroyState(u32 index) - { - if (index >= _states.size()) - return false; - - lua_State* state = _states[index]; - LuaStateCtx ctx(state); - - LuaManager* luaManager = ServiceLocator::GetLuaManager(); - auto gameEventHandler = luaManager->GetLuaHandler(LuaHandlerType::GameEvent); - gameEventHandler->ClearEvents(ctx.GetState()); - - ctx.Close(); - - return true; - } + //lua_State* LuaSystemBase::CreateState() + //{ + // LuaManager* luaManager = ServiceLocator::GetLuaManager(); + // + // LuaStateCtx ctx(luaL_newstate()); + // + // ctx.RegisterDefaultLibraries(); + // + // // Set Globals + // { + // const LuaTable& table = luaManager->GetGlobalTable(); + // ctx.SetGlobal(table); + // } + // + // ctx.MakeReadOnly(); + // + // const std::vector& bytecodeList = luaManager->GetBytecodeList(); + // for (u32 j = 0; j < bytecodeList.size(); j++) + // { + // const LuaBytecodeEntry& bytecodeEntry = bytecodeList[j]; + // std::string filePath = bytecodeEntry.filePath + "/" + bytecodeEntry.fileName; + // + // i32 result = ctx.LoadBytecode(filePath.c_str(), bytecodeEntry.bytecode, 0); + // if (result != LUA_OK) + // { + // ctx.Pop(); + // } + // } + // + // auto gameEventHandler = luaManager->GetLuaHandler(LuaHandlerType::GameEvent); + // + // lua_State* state = ctx.GetState(); + // gameEventHandler->SetupEvents(state); + // + // i32 top = ctx.GetTop(); + // for (i32 i = 0; i < top; i++) + // { + // ctx.Resume(); + // + // i32 result = ctx.GetStatus(); + // if (result != LUA_OK) + // { + // ctx.ReportError(); + // break; + // } + // } + // + // _states.push_back(state); + // return state; + //} + + //bool LuaSystemBase::DestroyState(u32 index) + //{ + // if (index >= _states.size()) + // return false; + // + // lua_State* state = _states[index]; + // LuaStateCtx ctx(state); + // + // LuaManager* luaManager = ServiceLocator::GetLuaManager(); + // auto gameEventHandler = luaManager->GetLuaHandler(LuaHandlerType::GameEvent); + // gameEventHandler->ClearEvents(ctx.GetState()); + // + // ctx.Close(); + // + // return true; + //} } \ No newline at end of file diff --git a/Source/Game/Game/Scripting/Systems/LuaSystemBase.h b/Source/Game/Game/Scripting/Systems/LuaSystemBase.h index 774e7d80..545d0bb4 100644 --- a/Source/Game/Game/Scripting/Systems/LuaSystemBase.h +++ b/Source/Game/Game/Scripting/Systems/LuaSystemBase.h @@ -11,23 +11,19 @@ namespace Scripting class LuaSystemBase { public: - LuaSystemBase(u32 numStates); + LuaSystemBase(); private: friend LuaManager; - void Init(u32 numStates); - void Update(f32 deltaTime); + void Init(); + void Update(f32 deltaTime, lua_State* state); void PushEvent(LuaSystemEvent systemEvent); protected: - virtual void Prepare(f32 deltaTime) = 0; - virtual void Run(f32 deltaTime, u32 index) = 0; + virtual void Prepare(f32 deltaTime, lua_State* state) = 0; + virtual void Run(f32 deltaTime, lua_State* state) = 0; - lua_State* CreateState(); - bool DestroyState(u32 index); - - std::vector _states; moodycamel::ConcurrentQueue _events; }; } \ No newline at end of file diff --git a/Source/Game/Game/Util/AnimationUtil.cpp b/Source/Game/Game/Util/AnimationUtil.cpp new file mode 100644 index 00000000..7ef4734a --- /dev/null +++ b/Source/Game/Game/Util/AnimationUtil.cpp @@ -0,0 +1,23 @@ +#include "AnimationUtil.h" + +#include "Game/ECS/Singletons/ClientDBCollection.h" +#include "Game/Util/ServiceLocator.h" + +#include + +#include + +namespace Util::Animation +{ + const ::ClientDB::Definitions::AnimationData* GetAnimationDataRec(entt::registry& registry, ::Animation::Type type) + { + auto& clientDBCollection = registry.ctx().get(); + auto* animationDatas = clientDBCollection.Get(ECS::Singletons::ClientDBHash::AnimationData); + + if (!animationDatas) + return nullptr; + + u32 typeIndex = static_cast(type); + return animationDatas->GetRow(typeIndex); + } +} diff --git a/Source/Game/Game/Util/AnimationUtil.h b/Source/Game/Game/Util/AnimationUtil.h new file mode 100644 index 00000000..533aca93 --- /dev/null +++ b/Source/Game/Game/Util/AnimationUtil.h @@ -0,0 +1,16 @@ +#pragma once +#include "Game/Animation/AnimationSystem.h" + +#include + +#include + +namespace ClientDB::Definitions +{ + struct AnimationData; +} + +namespace Util::Animation +{ + const ::ClientDB::Definitions::AnimationData* GetAnimationDataRec(entt::registry& registry, ::Animation::Type type); +} \ No newline at end of file diff --git a/Source/Game/Game/Util/CameraSaveUtil.h b/Source/Game/Game/Util/CameraSaveUtil.h index 3630b56a..fbf53f52 100644 --- a/Source/Game/Game/Util/CameraSaveUtil.h +++ b/Source/Game/Game/Util/CameraSaveUtil.h @@ -38,8 +38,8 @@ namespace Util::CameraSave return false; { - ECS::Components::Camera& camera = registry->get(activeCamera); - ECS::Components::Transform& transform = registry->get(activeCamera); + auto& camera = registry->get(activeCamera); + auto& transform = registry->get(activeCamera); vec3 position = transform.GetWorldPosition(); if (!buffer.Put(position)) @@ -85,8 +85,8 @@ namespace Util::CameraSave return false; { - ECS::Components::Camera& camera = registry->get(activeCamera); - ECS::Components::Transform& transform = registry->get(activeCamera); + auto& camera = registry->get(activeCamera); + auto& transform = registry->get(activeCamera); vec3 position; if (!buffer.Get(position)) diff --git a/Source/Game/Game/Util/CharacterControllerUtil.cpp b/Source/Game/Game/Util/CharacterControllerUtil.cpp new file mode 100644 index 00000000..e0d50b26 --- /dev/null +++ b/Source/Game/Game/Util/CharacterControllerUtil.cpp @@ -0,0 +1,87 @@ +#include "CharacterControllerUtil.h" + +#include +#include +#include + +void Util::CharacterController::Update(JPH::CharacterVirtual* characterVirtual, f32 deltaTime, JPH::Vec3& inGravity, const UpdateSettings& inSettings, const JPH::BroadPhaseLayerFilter& inBroadPhaseLayerFilter, const JPH::ObjectLayerFilter& inObjectLayerFilter, const JPH::BodyFilter& inBodyFilter, const JPH::ShapeFilter& inShapeFilter, JPH::TempAllocator& inAllocator) +{ + JPH::Vec3 stickToFloorStepDown = JPH::Vec3(inSettings.mStickToFloorStepDown.x, inSettings.mStickToFloorStepDown.y, inSettings.mStickToFloorStepDown.z); + JPH::Vec3 walkStairsStepUp = JPH::Vec3(inSettings.mWalkStairsStepUp.x, inSettings.mWalkStairsStepUp.y, inSettings.mWalkStairsStepUp.z); + JPH::Vec3 walkStairsStepDownExtra = JPH::Vec3(inSettings.mWalkStairsStepDownExtra.x, inSettings.mWalkStairsStepDownExtra.y, inSettings.mWalkStairsStepDownExtra.z); + + // Update the velocity + JPH::Vec3 desired_velocity = characterVirtual->GetLinearVelocity(); + characterVirtual->SetLinearVelocity(characterVirtual->CancelVelocityTowardsSteepSlopes(desired_velocity)); + + // Remember old position + JPH::RVec3 old_position = characterVirtual->GetPosition(); + + // Track if on ground before the update + bool ground_to_air = characterVirtual->IsSupported(); + + // Update the character position (instant, do not have to wait for physics update) + characterVirtual->Update(deltaTime, inGravity, inBroadPhaseLayerFilter, inObjectLayerFilter, inBodyFilter, inShapeFilter, inAllocator); + + // ... and that we got into air after + if (characterVirtual->IsSupported()) + ground_to_air = false; + + // If stick to floor enabled and we're going from supported to not supported + if (ground_to_air && !stickToFloorStepDown.IsNearZero()) + { + // If we're not moving up, stick to the floor + float velocity = JPH::Vec3(characterVirtual->GetPosition() - old_position).Dot(characterVirtual->GetUp()) / deltaTime; + if (velocity <= 1.0e-6f) + characterVirtual->StickToFloor(stickToFloorStepDown, inBroadPhaseLayerFilter, inObjectLayerFilter, inBodyFilter, inShapeFilter, inAllocator); + } + + // If walk stairs enabled + if (!walkStairsStepUp.IsNearZero()) + { + // Calculate how much we wanted to move horizontally + JPH::Vec3 desired_horizontal_step = desired_velocity * deltaTime; + desired_horizontal_step -= desired_horizontal_step.Dot(characterVirtual->GetUp()) * characterVirtual->GetUp(); + float desired_horizontal_step_len = desired_horizontal_step.Length(); + if (desired_horizontal_step_len > 0.0f) + { + // Calculate how much we moved horizontally + JPH::Vec3 achieved_horizontal_step = JPH::Vec3(characterVirtual->GetPosition() - old_position); + achieved_horizontal_step -= achieved_horizontal_step.Dot(characterVirtual->GetUp()) * characterVirtual->GetUp(); + + // Only count movement in the direction of the desired movement + // (otherwise we find it ok if we're sliding downhill while we're trying to climb uphill) + JPH::Vec3 step_forward_normalized = desired_horizontal_step / desired_horizontal_step_len; + achieved_horizontal_step = JPH::max(0.0f, achieved_horizontal_step.Dot(step_forward_normalized)) * step_forward_normalized; + float achieved_horizontal_step_len = achieved_horizontal_step.Length(); + + // If we didn't move as far as we wanted and we're against a slope that's too steep + if (achieved_horizontal_step_len + 1.0e-4f < desired_horizontal_step_len + && characterVirtual->CanWalkStairs(desired_velocity)) + { + // Calculate how much we should step forward + // Note that we clamp the step forward to a minimum distance. This is done because at very high frame rates the delta time + // may be very small, causing a very small step forward. If the step becomes small enough, we may not move far enough + // horizontally to actually end up at the top of the step. + JPH::Vec3 step_forward = step_forward_normalized * JPH::max(inSettings.mWalkStairsMinStepForward, desired_horizontal_step_len - achieved_horizontal_step_len); + + // Calculate how far to scan ahead for a floor. This is only used in case the floor normal at step_forward is too steep. + // In that case an additional check will be performed at this distance to check if that normal is not too steep. + // Start with the ground normal in the horizontal plane and normalizing it + JPH::Vec3 step_forward_test = -characterVirtual->GetGroundNormal(); + step_forward_test -= step_forward_test.Dot(characterVirtual->GetUp()) * characterVirtual->GetUp(); + step_forward_test = step_forward_test.NormalizedOr(step_forward_normalized); + + // If this normalized vector and the character forward vector is bigger than a preset angle, we use the character forward vector instead of the ground normal + // to do our forward test + if (step_forward_test.Dot(step_forward_normalized) < inSettings.mWalkStairsCosAngleForwardContact) + step_forward_test = step_forward_normalized; + + // Calculate the correct magnitude for the test vector + step_forward_test *= inSettings.mWalkStairsStepForwardTest; + + characterVirtual->WalkStairs(deltaTime, walkStairsStepUp, step_forward, step_forward_test, walkStairsStepDownExtra, inBroadPhaseLayerFilter, inObjectLayerFilter, inBodyFilter, inShapeFilter, inAllocator); + } + } + } +} diff --git a/Source/Game/Game/Util/CharacterControllerUtil.h b/Source/Game/Game/Util/CharacterControllerUtil.h new file mode 100644 index 00000000..200d03b7 --- /dev/null +++ b/Source/Game/Game/Util/CharacterControllerUtil.h @@ -0,0 +1,30 @@ +#pragma once +#include + +namespace JPH +{ + class BodyFilter; + class BroadPhaseLayerFilter; + class CharacterVirtual; + struct ExtendedUpdateSettings; + class ObjectLayerFilter; + class ShapeFilter; + class TempAllocator; + class Vec3; + +} + +namespace Util::CharacterController +{ + struct UpdateSettings + { + vec3 mStickToFloorStepDown{ 0, -1.0f, 0 }; ///< See StickToFloor inStepDown parameter. Can be zero to turn off. + vec3 mWalkStairsStepUp{ 0, 0.4f, 0 }; ///< See WalkStairs inStepUp parameter. Can be zero to turn off. + f32 mWalkStairsMinStepForward{ 0.02f }; ///< See WalkStairs inStepForward parameter. Note that the parameter only indicates a magnitude, direction is taken from current velocity. + f32 mWalkStairsStepForwardTest{ 0.1f }; ///< See WalkStairs inStepForwardTest parameter. Note that the parameter only indicates a magnitude, direction is taken from current velocity. + f32 mWalkStairsCosAngleForwardContact{ glm::cos(glm::radians(30.0f)) }; ///< Cos(angle) where angle is the maximum angle between the ground normal in the horizontal plane and the character forward vector where we're willing to adjust the step forward test towards the contact normal. + vec3 mWalkStairsStepDownExtra{ vec3(0.0f)}; ///< See WalkStairs inStepDownExtra + }; + + void Update(JPH::CharacterVirtual* characterVirtual, f32 deltaTime, JPH::Vec3& inGravity, const UpdateSettings& inSettings, const JPH::BroadPhaseLayerFilter& inBroadPhaseLayerFilter, const JPH::ObjectLayerFilter& inObjectLayerFilter, const JPH::BodyFilter& inBodyFilter, const JPH::ShapeFilter& inShapeFilter, JPH::TempAllocator& inAllocator); +} \ No newline at end of file diff --git a/Source/Game/Game/Util/PhysicsUtil.cpp b/Source/Game/Game/Util/PhysicsUtil.cpp index 98b7d29a..30bc451c 100644 --- a/Source/Game/Game/Util/PhysicsUtil.cpp +++ b/Source/Game/Game/Util/PhysicsUtil.cpp @@ -1,10 +1,12 @@ #include "PhysicsUtil.h" #include "Game/ECS/Components/Camera.h" +#include "Game/ECS/Components/NetworkedEntity.h" #include "Game/ECS/Singletons/ActiveCamera.h" #include "Game/ECS/Singletons/JoltState.h" #include "Game/ECS/Util/Transforms.h" #include "Game/Editor/Viewport.h" +#include "Game/Rendering/GameRenderer.h" #include #include @@ -26,14 +28,13 @@ namespace Util entt::registry* registry = ServiceLocator::GetEnttRegistries()->gameRegistry; entt::registry::context& ctx = registry->ctx(); - ECS::Singletons::ActiveCamera& activeCamera = ctx.get(); + auto& activeCamera = ctx.get(); if (activeCamera.entity == entt::null) return false; - ECS::Components::Transform& transform = registry->get(activeCamera.entity); - - ECS::Components::Camera& camera = registry->get(activeCamera.entity); + auto& transform = registry->get(activeCamera.entity); + auto& camera = registry->get(activeCamera.entity); vec4 mouseWorldPosition = camera.clipToWorld * vec4(mouseClipPosition, 0.0f, 1.0f); @@ -60,5 +61,58 @@ namespace Util return false; } + + bool GetEntityAtMousePosition(Editor::Viewport* viewport, entt::entity& entity) + { + vec2 mousePosition; + if (viewport->GetMousePosition(mousePosition)) + { + vec2 renderSize = ServiceLocator::GetGameRenderer()->GetRenderer()->GetRenderSize(); + vec2 mouseClipPosition = (vec2(mousePosition.x / renderSize.x, mousePosition.y / renderSize.y) * 2.0f) - 1.0f; + mouseClipPosition.y = -mouseClipPosition.y; + + entt::registry* registry = ServiceLocator::GetEnttRegistries()->gameRegistry; + entt::registry::context& ctx = registry->ctx(); + auto& activeCamera = ctx.get(); + + if (activeCamera.entity == entt::null) + return false; + + auto& transform = registry->get(activeCamera.entity); + auto& camera = registry->get(activeCamera.entity); + + vec4 mouseWorldPosition = camera.clipToWorld * vec4(mouseClipPosition, 0.0f, 1.0f); + + mouseWorldPosition.x /= mouseWorldPosition.w; + mouseWorldPosition.y /= mouseWorldPosition.w; + mouseWorldPosition.z /= mouseWorldPosition.w; + + auto& joltState = registry->ctx().get(); + + vec3 cameraWorldPosition = transform.GetWorldPosition(); + + JPH::Vec3 start = JPH::Vec3(cameraWorldPosition.x, cameraWorldPosition.y, cameraWorldPosition.z); + JPH::Vec3 direction = JPH::Vec3(mouseWorldPosition.x, mouseWorldPosition.y, mouseWorldPosition.z) - start; + + JPH::RRayCast ray(start, direction); + JPH::RayCastResult hit; + if (joltState.physicsSystem.GetNarrowPhaseQuery().CastRay(ray, hit)) + { + JPH::uint64 userData = joltState.physicsSystem.GetBodyInterface().GetUserData(hit.mBodyID); + + if (userData == std::numeric_limits().max()) + return false; + + entt::entity hitEntity = static_cast(userData); + if (!registry->valid(hitEntity)) + return false; + + entity = hitEntity; + return true; + } + } + + return false; + } } } \ No newline at end of file diff --git a/Source/Game/Game/Util/PhysicsUtil.h b/Source/Game/Game/Util/PhysicsUtil.h index 1f90108d..2a82722e 100644 --- a/Source/Game/Game/Util/PhysicsUtil.h +++ b/Source/Game/Game/Util/PhysicsUtil.h @@ -1,6 +1,8 @@ #pragma once #include +#include + struct ImGuiWindow; namespace Editor @@ -13,5 +15,6 @@ namespace Util namespace Physics { bool GetMouseWorldPosition(Editor::Viewport* viewport, vec3& mouseWorldPosition); + bool GetEntityAtMousePosition(Editor::Viewport* viewport, entt::entity& entity); } } \ No newline at end of file diff --git a/Source/Game/Game/Util/UnitUtil.cpp b/Source/Game/Game/Util/UnitUtil.cpp new file mode 100644 index 00000000..31b22d40 --- /dev/null +++ b/Source/Game/Game/Util/UnitUtil.cpp @@ -0,0 +1,168 @@ +#include "UnitUtil.h" + +#include "Game/Animation/AnimationSystem.h" +#include "Game/ECS/Components/CastInfo.h" +#include "Game/ECS/Components/Model.h" +#include "Game/ECS/Components/MovementInfo.h" +#include "Game/ECS/Components/UnitStatsComponent.h" +#include "Game/Util/AnimationUtil.h" +#include "Game/Util/ServiceLocator.h" + +#include + +#include + +using namespace ECS; + +namespace Util::Unit +{ + bool PlayAnimation(::Animation::InstanceID instanceID, ::Animation::Bone bone, ::Animation::Type animationID, ::Animation::Flag flags, ::Animation::BlendOverride blendOverride, ::Animation::AnimationCallback callback) + { + ::Animation::AnimationSystem* animationSystem = ServiceLocator::GetAnimationSystem(); + + if (!animationSystem->IsEnabled()) + { + return false; + } + + if (animationSystem->IsPlaying(instanceID, bone, animationID)) + return false; + + if (!animationSystem->SetBoneSequence(instanceID, bone, animationID, flags, blendOverride, callback)) + return false; + + return true; + } + + bool UpdateAnimationState(entt::registry& registry, entt::entity entity, ::Animation::InstanceID instanceID, f32 deltaTime) + { + ::Animation::AnimationSystem* animationSystem = ServiceLocator::GetAnimationSystem(); + if (!animationSystem->IsEnabled()) + { + return false; + } + + if (instanceID == std::numeric_limits().max()) + return false; + + ::Animation::AnimationSequenceState currentAnimation = { }; + ::Animation::AnimationSequenceState nextAnimation = { }; + animationSystem->GetCurrentAnimation(instanceID, ::Animation::Bone::Default, ¤tAnimation, &nextAnimation); + + const ClientDB::Definitions::AnimationData* currentAnimationData = Util::Animation::GetAnimationDataRec(registry, currentAnimation.animation); + + auto& unitStatsComponent = registry.get(entity); + + bool isAlive = unitStatsComponent.currentHealth > 0.0f; + if (!isAlive) + { + return PlayAnimation(instanceID, ::Animation::Bone::Default, ::Animation::Type::Death, ::Animation::Flag::Freeze, ::Animation::BlendOverride::Start); + } + + if (auto* castInfo = registry.try_get(entity)) + { + if (currentAnimation.animation != ::Animation::Type::SpellCastDirected) + { + castInfo->duration = glm::min(castInfo->duration + 1.0f * deltaTime, castInfo->castTime); + + if (castInfo->castTime > 0.0f && (castInfo->castTime != castInfo->duration)) + { + return PlayAnimation(instanceID, ::Animation::Bone::Default, ::Animation::Type::ReadySpellDirected, ::Animation::Flag::Loop, ::Animation::BlendOverride::Start); + } + else if (castInfo->castTime == castInfo->duration) + { + return PlayAnimation(instanceID, ::Animation::Bone::Default, ::Animation::Type::SpellCastDirected, ::Animation::Flag::Freeze, ::Animation::BlendOverride::Start); + } + } + else + { + if ((u32)currentAnimation.flags & (u32)::Animation::Flag::Frozen) + { + registry.erase(entity); + } + } + + return true; + } + + auto& movementInfo = registry.get(entity); + + bool isMovingForward = movementInfo.movementFlags.forward; + bool isMovingBackward = movementInfo.movementFlags.backward; + bool isMovingLeft = movementInfo.movementFlags.left; + bool isMovingRight = movementInfo.movementFlags.right; + bool isMoving = isMovingForward || isMovingBackward || isMovingLeft || isMovingRight; + + if (movementInfo.jumpState == Components::JumpState::Begin) + { + if (PlayAnimation(instanceID, ::Animation::Bone::Default, ::Animation::Type::JumpStart, ::Animation::Flag::Freeze, ::Animation::BlendOverride::Start)) + { + movementInfo.jumpState = Components::JumpState::Jumping; + } + } + + if (movementInfo.jumpState == Components::JumpState::Jumping) + { + if (movementInfo.verticalVelocity > 0.0f) + return true; + + movementInfo.jumpState = Components::JumpState::Fall; + } + + if (!movementInfo.movementFlags.grounded) + { + if (currentAnimationData) + { + auto behaviorID = static_cast<::Animation::Type>(currentAnimationData->behaviorID); + + if (behaviorID >= ::Animation::Type::JumpStart && behaviorID <= ::Animation::Type::Fall) + { + return true; + } + } + + return PlayAnimation(instanceID, ::Animation::Bone::Default, ::Animation::Type::Fall, ::Animation::Flag::Loop, ::Animation::BlendOverride::Start); + } + else if (isMovingBackward) + { + return PlayAnimation(instanceID, ::Animation::Bone::Default, ::Animation::Type::Walkbackwards, ::Animation::Flag::Loop, ::Animation::BlendOverride::Start); + } + else if (isMovingForward || isMovingLeft || isMovingRight) + { + if ((currentAnimation.animation == ::Animation::Type::JumpLandRun && ((u32)currentAnimation.flags & (u32)::Animation::Flag::Frozen) == 0) || nextAnimation.animation == ::Animation::Type::JumpLandRun) + { + return true; + } + + if (movementInfo.movementFlags.justEndedJump) + { + return PlayAnimation(instanceID, ::Animation::Bone::Default, ::Animation::Type::JumpLandRun, ::Animation::Flag::Freeze, ::Animation::BlendOverride::Start); + } + + ::Animation::Type animation = ::Animation::Type::Run; + + if (movementInfo.speed >= 11.0f) + { + animation = ::Animation::Type::Sprint; + } + + return PlayAnimation(instanceID, ::Animation::Bone::Default, animation, ::Animation::Flag::Loop, ::Animation::BlendOverride::Start); + } + else + { + if ((currentAnimation.animation == ::Animation::Type::JumpEnd && ((u32)currentAnimation.flags & (u32)::Animation::Flag::Frozen) == 0) || nextAnimation.animation == ::Animation::Type::JumpEnd) + { + return true; + } + + if (movementInfo.movementFlags.justEndedJump) + { + return PlayAnimation(instanceID, ::Animation::Bone::Default, ::Animation::Type::JumpEnd, ::Animation::Flag::Freeze, ::Animation::BlendOverride::Start); + } + + return PlayAnimation(instanceID, ::Animation::Bone::Default, ::Animation::Type::Stand, ::Animation::Flag::Loop, ::Animation::BlendOverride::Start); + } + + return true; + } +} diff --git a/Source/Game/Game/Util/UnitUtil.h b/Source/Game/Game/Util/UnitUtil.h new file mode 100644 index 00000000..3397a195 --- /dev/null +++ b/Source/Game/Game/Util/UnitUtil.h @@ -0,0 +1,12 @@ +#pragma once +#include "Game/Animation/AnimationSystem.h" + +#include + +#include + +namespace Util::Unit +{ + bool PlayAnimation(Animation::InstanceID instanceID, Animation::Bone bone, Animation::Type animationID, Animation::Flag flags = Animation::Flag::Loop, Animation::BlendOverride blendOverride = Animation::BlendOverride::Auto, Animation::AnimationCallback callback = nullptr); + bool UpdateAnimationState(entt::registry& registry, entt::entity entity, Animation::InstanceID instanceID, f32 deltaTime); +} \ No newline at end of file diff --git a/Source/ShaderCookerStandalone/CMakeLists.txt b/Source/ShaderCookerStandalone/CMakeLists.txt deleted file mode 100644 index 06481cd9..00000000 --- a/Source/ShaderCookerStandalone/CMakeLists.txt +++ /dev/null @@ -1,34 +0,0 @@ -project(shadercookerstandalone VERSION 1.0.0 DESCRIPTION "Standalone Shader Cooker for compiling shaders") - -file(GLOB_RECURSE SHADER_COOKER_STANDALONE_FILES "*.cpp" "*.h") - -add_executable(${PROJECT_NAME} ${SHADER_COOKER_STANDALONE_FILES}) -set_target_properties(${PROJECT_NAME} PROPERTIES FOLDER ${ROOT_FOLDER}) -set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD 17) - -create_vs_filters(${SHADER_COOKER_STANDALONE_FILES}) - -include_directories(include/) - -if(APPLE) - target_link_libraries(${PROJECT_NAME} PRIVATE c++fs) -elseif (UNIX) - target_link_libraries(${PROJECT_NAME} PRIVATE stdc++fs) -endif() - -add_compile_definitions(NOMINMAX _SILENCE_ALL_CXX17_DEPRECATION_WARNINGS) - -target_link_libraries(${PROJECT_NAME} PRIVATE - base::base - shadercooker::shadercooker -) - -install(TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) - -add_custom_command( - TARGET shadercookerstandalone - POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy - "${DXCOMPILER_DYNAMIC_LIB_PATH}" - "$/dxcompiler.dll" -) \ No newline at end of file diff --git a/Source/Shaders/Shaders/Model/Draw.ps.hlsl b/Source/Shaders/Shaders/Model/Draw.ps.hlsl index 4d55d464..84922470 100644 --- a/Source/Shaders/Shaders/Model/Draw.ps.hlsl +++ b/Source/Shaders/Shaders/Model/Draw.ps.hlsl @@ -7,8 +7,6 @@ permutation SUPPORTS_EXTENDED_TEXTURES = [0, 1]; #include "Model/Shared.inc.hlsl" #include "Include/VisibilityBuffers.inc.hlsl" -[[vk::binding(10, MODEL)]] SamplerState _sampler; - struct PSInput { uint drawID : TEXCOORD0; @@ -41,20 +39,22 @@ PSOutput main(PSInput input) if (blendingMode != 1) // ALPHA KEY continue; - + + uint texture0SamplerIndex = (textureUnit.data1 >> 1) & 0x3; + uint texture1SamplerIndex = (textureUnit.data1 >> 3) & 0x3; uint materialType = (textureUnit.data1 >> 16) & 0xFFFF; if (materialType == 0x8000) continue; - float4 texture1 = _modelTextures[NonUniformResourceIndex(textureUnit.textureIDs[0])].Sample(_sampler, input.uv01.xy); + float4 texture1 = _modelTextures[NonUniformResourceIndex(textureUnit.textureIDs[0])].Sample(_samplers[texture0SamplerIndex], input.uv01.xy); float4 texture2 = float4(1, 1, 1, 1); uint vertexShaderId = materialType & 0xFF; if (vertexShaderId > 2) { // ENV uses generated UVCoords based on camera pos + geometry normal in frame space - texture2 = _modelTextures[NonUniformResourceIndex(textureUnit.textureIDs[1])].Sample(_sampler, input.uv01.zw); + texture2 = _modelTextures[NonUniformResourceIndex(textureUnit.textureIDs[1])].Sample(_samplers[texture1SamplerIndex], input.uv01.zw); } // Experimental alphakey discard without shading, if this has issues check github history for cModel.ps.hlsl diff --git a/Source/Shaders/Shaders/Model/DrawSkybox.ps.hlsl b/Source/Shaders/Shaders/Model/DrawSkybox.ps.hlsl index f31b6a7e..24f5d665 100644 --- a/Source/Shaders/Shaders/Model/DrawSkybox.ps.hlsl +++ b/Source/Shaders/Shaders/Model/DrawSkybox.ps.hlsl @@ -4,8 +4,7 @@ permutation SUPPORTS_EXTENDED_TEXTURES = [0, 1]; #include "common.inc.hlsl" #include "globalData.inc.hlsl" #include "Model/Shared.inc.hlsl" - -[[vk::binding(10, MODEL)]] SamplerState _sampler; +#include "Include/OIT.inc.hlsl" struct PSInput { @@ -16,7 +15,8 @@ struct PSInput struct PSOutput { - float4 color : SV_Target0; + float4 transparency : SV_Target0; + float4 transparencyWeight : SV_Target1; }; PSOutput main(PSInput input) @@ -35,6 +35,8 @@ PSOutput main(PSInput input) ModelTextureUnit textureUnit = _modelTextureUnits[textureUnitIndex]; uint isProjectedTexture = textureUnit.data1 & 0x1; + uint texture0SamplerIndex = (textureUnit.data1 >> 1) & 0x3; + uint texture1SamplerIndex = (textureUnit.data1 >> 3) & 0x3; uint materialFlags = (textureUnit.data1 >> 1) & 0x3FF; uint blendingMode = (textureUnit.data1 >> 11) & 0x7; @@ -45,13 +47,13 @@ PSOutput main(PSInput input) if (materialType == 0x8000) continue; - float4 texture0Color = _modelTextures[NonUniformResourceIndex(textureUnit.textureIDs[0])].Sample(_sampler, input.uv01.xy); + float4 texture0Color = _modelTextures[NonUniformResourceIndex(textureUnit.textureIDs[0])].Sample(_samplers[texture0SamplerIndex], input.uv01.xy); float4 texture1Color = float4(1, 1, 1, 1); if (vertexShaderId > 2) { // ENV uses generated UVCoords based on camera pos + geometry normal in frame space - texture1Color = _modelTextures[NonUniformResourceIndex(textureUnit.textureIDs[1])].Sample(_sampler, input.uv01.zw); + texture1Color = _modelTextures[NonUniformResourceIndex(textureUnit.textureIDs[1])].Sample(_samplers[texture1SamplerIndex], input.uv01.zw); } float4 shadedColor = ShadeModel(pixelShaderId, texture0Color, texture1Color, specular); @@ -75,9 +77,13 @@ PSOutput main(PSInput input) float biggestComponent = max(color.x, max(color.y, color.z)); color.a = biggestComponent * (blendingMode == 4) + color.a * (blendingMode != 4); - + + float oitDepth = input.position.z / input.position.w; + float oitWeight = CalculateOITWeight(color, oitDepth); + PSOutput output; - output.color = color; + output.transparency = float4(color.rgb * color.a, color.a) * oitWeight; + output.transparencyWeight.a = color.a; return output; } \ No newline at end of file diff --git a/Source/Shaders/Shaders/Model/DrawSkybox.vs.hlsl b/Source/Shaders/Shaders/Model/DrawSkybox.vs.hlsl index 0fde2174..c077ffca 100644 --- a/Source/Shaders/Shaders/Model/DrawSkybox.vs.hlsl +++ b/Source/Shaders/Shaders/Model/DrawSkybox.vs.hlsl @@ -25,8 +25,20 @@ VSOutput main(VSInput input) ModelDrawCallData drawCallData = LoadModelDrawCallData(drawCallID); ModelInstanceData instanceData = _modelInstanceDatas[drawCallData.instanceID]; float4x4 instanceMatrix = _modelInstanceMatrices[drawCallData.instanceID]; + + // Skin this vertex + float4x4 boneTransformMatrix = CalcBoneTransformMatrix(instanceData, vertex); + float4 position = mul(float4(vertex.position, 1.0f), boneTransformMatrix); + + // Save the skinned vertex position (in model-space) if this vertex was animated + if (instanceData.boneMatrixOffset != 4294967295) + { + uint localVertexID = input.vertexID - instanceData.modelVertexOffset; // This gets the local vertex ID relative to the model + uint animatedVertexID = localVertexID + instanceData.animatedVertexOffset; // This makes it relative to the animated instance - float4 position = float4(vertex.position, 1.0f); + StoreAnimatedVertexPosition(animatedVertexID, position.xyz); + } + position = mul(position, instanceMatrix); float4 UVs = vertex.uv01; diff --git a/Source/Shaders/Shaders/Model/DrawTransparent.ps.hlsl b/Source/Shaders/Shaders/Model/DrawTransparent.ps.hlsl index 3956ffba..f87e3777 100644 --- a/Source/Shaders/Shaders/Model/DrawTransparent.ps.hlsl +++ b/Source/Shaders/Shaders/Model/DrawTransparent.ps.hlsl @@ -6,8 +6,6 @@ permutation SUPPORTS_EXTENDED_TEXTURES = [0, 1]; #include "Model/Shared.inc.hlsl" #include "Include/OIT.inc.hlsl" -[[vk::binding(10, MODEL)]] SamplerState _sampler; - struct PSInput { float4 position : SV_Position; @@ -38,6 +36,8 @@ PSOutput main(PSInput input) ModelTextureUnit textureUnit = _modelTextureUnits[textureUnitIndex]; uint isProjectedTexture = textureUnit.data1 & 0x1; + uint texture0SamplerIndex = (textureUnit.data1 >> 1) & 0x3; + uint texture1SamplerIndex = (textureUnit.data1 >> 3) & 0x3; uint materialFlags = (textureUnit.data1 >> 1) & 0x3FF; uint blendingMode = (textureUnit.data1 >> 11) & 0x7; @@ -48,13 +48,13 @@ PSOutput main(PSInput input) if (materialType == 0x8000) continue; - float4 texture0Color = _modelTextures[NonUniformResourceIndex(textureUnit.textureIDs[0])].Sample(_sampler, input.uv01.xy); + float4 texture0Color = _modelTextures[NonUniformResourceIndex(textureUnit.textureIDs[0])].Sample(_samplers[texture0SamplerIndex], input.uv01.xy); float4 texture1Color = float4(1, 1, 1, 1); if (vertexShaderId > 2) { // ENV uses generated UVCoords based on camera pos + geometry normal in frame space - texture1Color = _modelTextures[NonUniformResourceIndex(textureUnit.textureIDs[1])].Sample(_sampler, input.uv01.zw); + texture1Color = _modelTextures[NonUniformResourceIndex(textureUnit.textureIDs[1])].Sample(_samplers[texture1SamplerIndex], input.uv01.zw); } float4 shadedColor = ShadeModel(pixelShaderId, texture0Color, texture1Color, specular); diff --git a/Source/Shaders/Shaders/Model/DrawTransparent.vs.hlsl b/Source/Shaders/Shaders/Model/DrawTransparent.vs.hlsl index dd4ff584..32f426d0 100644 --- a/Source/Shaders/Shaders/Model/DrawTransparent.vs.hlsl +++ b/Source/Shaders/Shaders/Model/DrawTransparent.vs.hlsl @@ -28,21 +28,18 @@ VSOutput main(VSInput input) float4x4 instanceMatrix = _modelInstanceMatrices[drawCallData.instanceID]; // Skin this vertex - //float4x4 boneTransformMatrix = CalcBoneTransformMatrix(instanceData, vertex); - - /* TODO: Animation + float4x4 boneTransformMatrix = CalcBoneTransformMatrix(instanceData, vertex); float4 position = mul(float4(vertex.position, 1.0f), boneTransformMatrix); // Save the skinned vertex position (in model-space) if this vertex was animated - /if (instanceData.boneDeformOffset != 4294967295) + if (instanceData.boneMatrixOffset != 4294967295) { uint localVertexID = input.vertexID - instanceData.modelVertexOffset; // This gets the local vertex ID relative to the model uint animatedVertexID = localVertexID + instanceData.animatedVertexOffset; // This makes it relative to the animated instance StoreAnimatedVertexPosition(animatedVertexID, position.xyz); - }*/ - - float4 position = float4(vertex.position, 1.0f); + } + position = mul(position, instanceMatrix); float4 UVs = vertex.uv01; diff --git a/Source/Shaders/Shaders/Model/Shared.inc.hlsl b/Source/Shaders/Shaders/Model/Shared.inc.hlsl index 7659decd..392029bd 100644 --- a/Source/Shaders/Shaders/Model/Shared.inc.hlsl +++ b/Source/Shaders/Shaders/Model/Shared.inc.hlsl @@ -2,6 +2,8 @@ #define MODEL_SHARED_INCLUDED #include "common.inc.hlsl" +#define MAX_MODEL_SAMPLERS 4 + struct ModelInstanceData { uint modelID; @@ -209,6 +211,7 @@ struct ModelTextureUnit }; [[vk::binding(9, MODEL)]] StructuredBuffer _modelTextureUnits; +[[vk::binding(10, MODEL)]] SamplerState _samplers[MAX_MODEL_SAMPLERS]; [[vk::binding(20, MODEL)]] Texture2D _modelTextures[MAX_TEXTURES]; // We give this index 20 because it always needs to be last in this descriptor set enum ModelPixelShaderID diff --git a/Source/Shaders/Shaders/materialPass.cs.hlsl b/Source/Shaders/Shaders/materialPass.cs.hlsl index 3c130bab..ef5f4926 100644 --- a/Source/Shaders/Shaders/materialPass.cs.hlsl +++ b/Source/Shaders/Shaders/materialPass.cs.hlsl @@ -9,6 +9,7 @@ permutation EDITOR_MODE = [0, 1]; // Off, Terrain #include "Include/Editor.inc.hlsl" #include "Include/Lighting.inc.hlsl" #include "Terrain/Shared.inc.hlsl" +#include "Model/Shared.inc.hlsl" // Reenable this in C++ as well struct Constants @@ -269,7 +270,9 @@ float4 ShadeModel(const uint2 pixelPos, const float2 screenUV, const VisibilityB ModelTextureUnit textureUnit = _modelTextureUnits[textureUnitIndex]; uint isProjectedTexture = textureUnit.data1 & 0x1; - uint materialFlags = (textureUnit.data1 >> 1) & 0x3FF; + uint texture0SamplerIndex = (textureUnit.data1 >> 1) & 0x3; + uint texture1SamplerIndex = (textureUnit.data1 >> 3) & 0x3; + uint materialFlags = (textureUnit.data1 >> 5) & 0x3F; uint blendingMode = (textureUnit.data1 >> 11) & 0x7; uint materialType = (textureUnit.data1 >> 16) & 0xFFFF; @@ -279,13 +282,13 @@ float4 ShadeModel(const uint2 pixelPos, const float2 screenUV, const VisibilityB if (materialType == 0x8000) continue; - float4 texture0Color = _modelTextures[NonUniformResourceIndex(textureUnit.textureIDs[0])].SampleGrad(_sampler, pixelUV0.value, pixelUV0.ddx, pixelUV0.ddy); + float4 texture0Color = _modelTextures[NonUniformResourceIndex(textureUnit.textureIDs[0])].SampleGrad(_samplers[texture0SamplerIndex], pixelUV0.value, pixelUV0.ddx, pixelUV0.ddy); float4 texture1Color = float4(0, 0, 0, 0); if (vertexShaderId >= 2) { // ENV uses generated UVCoords based on camera pos + geometry normal in frame space - texture1Color = _modelTextures[NonUniformResourceIndex(textureUnit.textureIDs[1])].SampleGrad(_sampler, pixelUV1.value, pixelUV1.ddx, pixelUV1.ddy); + texture1Color = _modelTextures[NonUniformResourceIndex(textureUnit.textureIDs[1])].SampleGrad(_samplers[texture1SamplerIndex], pixelUV1.value, pixelUV1.ddx, pixelUV1.ddy); } isUnlit |= (materialFlags & 0x1); @@ -297,6 +300,7 @@ float4 ShadeModel(const uint2 pixelPos, const float2 screenUV, const VisibilityB // Apply lighting color.rgb = ApplyLighting(color.rgb, pixelNormal, screenUV, pixelPos, _constants.lightInfo); //color = float4(pixelUV0.value, 0, 1); + return saturate(color); } diff --git a/Submodules/Engine b/Submodules/Engine index 332027d2..5af2ad41 160000 --- a/Submodules/Engine +++ b/Submodules/Engine @@ -1 +1 @@ -Subproject commit 332027d24fa04c7c91878cd88298f7f7f0b5d1e6 +Subproject commit 5af2ad419d0479c41db51558835047e133ad3d0b