Checking in what I found on my hard drive
This commit is contained in:
1
.gitattributes
vendored
1
.gitattributes
vendored
@@ -1,5 +1,6 @@
|
|||||||
* text=auto
|
* text=auto
|
||||||
*.bat text eol=crlf
|
*.bat text eol=crlf
|
||||||
|
*.sh text eol=lf
|
||||||
*.lib filter=lfs diff=lfs merge=lfs -text
|
*.lib filter=lfs diff=lfs merge=lfs -text
|
||||||
*.exe filter=lfs diff=lfs merge=lfs -text
|
*.exe filter=lfs diff=lfs merge=lfs -text
|
||||||
*.a filter=lfs diff=lfs merge=lfs -text
|
*.a filter=lfs diff=lfs merge=lfs -text
|
||||||
|
|||||||
7
.gitignore
vendored
7
.gitignore
vendored
@@ -19,6 +19,13 @@ gmon.out
|
|||||||
*.pdb
|
*.pdb
|
||||||
*.sln
|
*.sln
|
||||||
*.vcproj
|
*.vcproj
|
||||||
|
.ignore
|
||||||
|
|
||||||
|
Integration.code-workspace
|
||||||
|
Makefile
|
||||||
|
|
||||||
|
Source/Integration/lpx-*.hpp
|
||||||
|
Source/Integration/lpx-*.cpp
|
||||||
|
|
||||||
.vscode/**
|
.vscode/**
|
||||||
Saved/**
|
Saved/**
|
||||||
|
|||||||
BIN
Content/MeshStruct.uasset
LFS
Normal file
BIN
Content/MeshStruct.uasset
LFS
Normal file
Binary file not shown.
BIN
Content/NameToMeshTable.uasset
LFS
Normal file
BIN
Content/NameToMeshTable.uasset
LFS
Normal file
Binary file not shown.
BIN
Content/TangibleActor.uasset
LFS
BIN
Content/TangibleActor.uasset
LFS
Binary file not shown.
@@ -2,9 +2,11 @@
|
|||||||
#include "AnimQueue.h"
|
#include "AnimQueue.h"
|
||||||
|
|
||||||
FlxAnimationStep::FlxAnimationStep(uint64 hash, std::string_view body) {
|
FlxAnimationStep::FlxAnimationStep(uint64 hash, std::string_view body) {
|
||||||
|
Finished = false;
|
||||||
Hash = hash;
|
Hash = hash;
|
||||||
Body.SetNum(body.size());
|
Body.SetNum(body.size());
|
||||||
memcpy(Body.GetData(), body.data(), body.size());
|
memcpy(Body.GetData(), body.data(), body.size());
|
||||||
|
Blueprint = UlxAnimationStepLibrary::AnimationStepGetString(*this, "bp");
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool ClearProperties(const FString& prefix, UObject* obj) {
|
static bool ClearProperties(const FString& prefix, UObject* obj) {
|
||||||
@@ -27,32 +29,35 @@ static bool ClearProperties(const FString& prefix, UObject* obj) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool SetProperty(const FString& name, UObject* obj, const FlxAnimationField& field) {
|
#pragma optimize("", off)
|
||||||
|
|
||||||
|
static bool SetProperty(const FString& prefix, UObject* obj, const FlxAnimationField& field) {
|
||||||
UClass* uclass = obj->GetClass();
|
UClass* uclass = obj->GetClass();
|
||||||
FName nname(name);
|
FString sname(field.Name.size(), (const UTF8CHAR*)field.Name.data());
|
||||||
|
FName nname(prefix + sname);
|
||||||
switch (field.Type) {
|
switch (field.Type) {
|
||||||
case ElxAnimValueType::STRING: {
|
case SimpleDynamicTag::STRING: {
|
||||||
FStrProperty* fprop = FindFProperty<FStrProperty>(uclass, nname);
|
FStrProperty* fprop = FindFProperty<FStrProperty>(uclass, nname);
|
||||||
if (fprop == nullptr) return false;
|
if (fprop == nullptr) return false;
|
||||||
FString* pptr = fprop->ContainerPtrToValuePtr<FString>(obj);
|
FString* pptr = fprop->ContainerPtrToValuePtr<FString>(obj);
|
||||||
*pptr = FString(field.S.size(), (const UTF8CHAR*)field.S.data());
|
*pptr = FString(field.S.size(), (const UTF8CHAR*)field.S.data());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case ElxAnimValueType::NUMBER: {
|
case SimpleDynamicTag::NUMBER: {
|
||||||
FDoubleProperty* fprop = FindFProperty<FDoubleProperty>(uclass, nname);
|
FDoubleProperty* fprop = FindFProperty<FDoubleProperty>(uclass, nname);
|
||||||
if (fprop == nullptr) return false;
|
if (fprop == nullptr) return false;
|
||||||
double* pptr = fprop->ContainerPtrToValuePtr<double>(obj);
|
double* pptr = fprop->ContainerPtrToValuePtr<double>(obj);
|
||||||
fprop->SetPropertyValue(pptr, field.X);
|
*pptr = field.X;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case ElxAnimValueType::BOOLEAN: {
|
case SimpleDynamicTag::BOOLEAN: {
|
||||||
FBoolProperty* fprop = FindFProperty<FBoolProperty>(uclass, nname);
|
FBoolProperty* fprop = FindFProperty<FBoolProperty>(uclass, nname);
|
||||||
if (fprop == nullptr) return false;
|
if (fprop == nullptr) return false;
|
||||||
uint8* pptr = fprop->ContainerPtrToValuePtr<uint8>(obj);
|
uint8* pptr = fprop->ContainerPtrToValuePtr<uint8>(obj);
|
||||||
fprop->SetPropertyValue(pptr, (field.X == 1.0));
|
fprop->SetPropertyValue(pptr, (field.X == 1.0));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case ElxAnimValueType::XYZ: {
|
case SimpleDynamicTag::VECTOR: {
|
||||||
FStructProperty* fprop = FindFProperty<FStructProperty>(uclass, nname);
|
FStructProperty* fprop = FindFProperty<FStructProperty>(uclass, nname);
|
||||||
if (fprop == nullptr) return false;
|
if (fprop == nullptr) return false;
|
||||||
if (fprop->Struct != TBaseStructure<FVector>::Get()) return false;
|
if (fprop->Struct != TBaseStructure<FVector>::Get()) return false;
|
||||||
@@ -64,73 +69,179 @@ static bool SetProperty(const FString& name, UObject* obj, const FlxAnimationFie
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FlxAnimationStep::Unpack(const FString& prefix, UObject* into, bool preclear) const {
|
#pragma optimize("", off)
|
||||||
|
bool FlxAnimationStep::Unpack(const FString& prefix, UObject* into) const {
|
||||||
UClass* uclass = into->GetClass();
|
UClass* uclass = into->GetClass();
|
||||||
std::string_view body((const char*)(Body.GetData()), Body.Num());
|
std::string_view body((const char*)(Body.GetData()), Body.Num());
|
||||||
FlxAnimationStepDecoder decoder(body);
|
FlxAnimationStepDecoder decoder(body);
|
||||||
bool ok = true;
|
bool ok = ClearProperties(prefix, into);
|
||||||
if (preclear) {
|
|
||||||
ok &= ClearProperties(prefix, into);
|
|
||||||
}
|
|
||||||
while (!decoder.AtEOF()) {
|
while (!decoder.AtEOF()) {
|
||||||
FlxAnimationField field = decoder.ReadField();
|
FlxAnimationField field = decoder.ReadField();
|
||||||
FString sname(field.Name.size(), (const UTF8CHAR*)field.Name.data());
|
if (Finished && !field.Persistent) continue;
|
||||||
ok &= SetProperty(prefix + sname, into, field);
|
ok &= SetProperty(prefix, into, field);
|
||||||
|
}
|
||||||
|
if (Finished) {
|
||||||
|
FlxAnimationField field;
|
||||||
|
field.Name = "action";
|
||||||
|
field.Persistent = false;
|
||||||
|
field.Type = SimpleDynamicTag::STRING;
|
||||||
|
field.S = "idle";
|
||||||
|
ok &= SetProperty(prefix, into, field);
|
||||||
}
|
}
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
FString UlxAnimationStepLibrary::AnimationStepDebugString(const FlxAnimationStep& step) {
|
FString UlxAnimationStepLibrary::AnimationStepDebugString(const FlxAnimationStep& step) {
|
||||||
std::string_view body((const char*)(step.Body.GetData()), step.Body.Num());
|
std::string_view body((const char*)(step.Body.GetData()), step.Body.Num());
|
||||||
return FlxAnimationStepDecoder::DebugString(step.Hash, body);
|
return FlxAnimationStepDecoder::DebugString(step.Finished, step.Finished, step.Hash, body);
|
||||||
}
|
}
|
||||||
|
|
||||||
void UlxAnimationStepLibrary::UnpackAnimationStep(const FlxAnimationStep& step,
|
void UlxAnimationStepLibrary::UnpackAnimationStep(UObject* into, const FlxAnimationStep& step, const FString& prefix) {
|
||||||
const FString& prefix, UObject* into) {
|
step.Unpack(prefix, into);
|
||||||
step.Unpack(prefix, into, true);
|
}
|
||||||
};
|
|
||||||
|
|
||||||
static FlxAnimationField FindAnimationField(const FlxAnimationStep& step, const FString& name) {
|
static FlxAnimationField FindAnimationFieldLL(const FlxAnimationStep& step, std::string_view name) {
|
||||||
std::string_view body((const char*)(step.Body.GetData()), step.Body.Num());
|
std::string_view body((const char*)(step.Body.GetData()), step.Body.Num());
|
||||||
FTCHARToUTF8 utf8name(name);
|
|
||||||
std::string_view uname(utf8name.Get(), utf8name.Length());
|
|
||||||
FlxAnimationStepDecoder decoder(body);
|
FlxAnimationStepDecoder decoder(body);
|
||||||
FlxAnimationField result;
|
FlxAnimationField result;
|
||||||
while (!decoder.AtEOF()) {
|
while (!decoder.AtEOF()) {
|
||||||
result = decoder.ReadField();
|
result = decoder.ReadField();
|
||||||
if (result.Name == uname) {
|
if (result.Name == name) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result.Type = ElxAnimValueType::INVALID;
|
result.Type = SimpleDynamicTag::UNINITIALIZED;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static FlxAnimationField FindAnimationField(const FlxAnimationStep& step, const FString& name) {
|
||||||
|
FTCHARToUTF8 utf8name(name);
|
||||||
|
std::string_view uname(utf8name.Get(), utf8name.Length());
|
||||||
|
return FindAnimationFieldLL(step, uname);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlxAnimationStep::AutoUpdateXYZ(AActor *actor) const {
|
||||||
|
FlxAnimationField xyz = FindAnimationFieldLL(*this, "xyz");
|
||||||
|
if (xyz.Type == SimpleDynamicTag::VECTOR) {
|
||||||
|
actor->SetActorLocation(FVector(xyz.X, xyz.Y, xyz.Z));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlxAnimationStep::AutoUpdateFacing(AActor *actor) const {
|
||||||
|
FlxAnimationField facing = FindAnimationFieldLL(*this, "facing");
|
||||||
|
if (facing.Type == SimpleDynamicTag::NUMBER) {
|
||||||
|
actor->SetActorRotation(FRotator(0, facing.X, 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlxAnimationStep::AutoUpdatePlane(FName *planep) const {
|
||||||
|
FlxAnimationField plane = FindAnimationFieldLL(*this, "plane");
|
||||||
|
if (plane.Type == SimpleDynamicTag::STRING) {
|
||||||
|
FString pname(plane.S.size(), (const UTF8CHAR*)(plane.S.data()));
|
||||||
|
*planep = FName(pname);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UlxAnimationStepLibrary::AnimationStepEqual(const FlxAnimationStep &stepa, const FlxAnimationStep &stepb) {
|
||||||
|
return (stepa.Finished == stepb.Finished) && (stepa.Hash == stepb.Hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UlxAnimationStepLibrary::AnimationStepIsIdle(const FlxAnimationStep &step) {
|
||||||
|
return step.Finished;
|
||||||
|
}
|
||||||
|
|
||||||
FVector UlxAnimationStepLibrary::AnimationStepGetVector(const FlxAnimationStep& step, const FString& name) {
|
FVector UlxAnimationStepLibrary::AnimationStepGetVector(const FlxAnimationStep& step, const FString& name) {
|
||||||
FlxAnimationField field = FindAnimationField(step, name);
|
FlxAnimationField field = FindAnimationField(step, name);
|
||||||
if (field.Type != ElxAnimValueType::XYZ) return FVector(0, 0, 0);
|
if (field.Type != SimpleDynamicTag::VECTOR) return FVector(0, 0, 0);
|
||||||
return FVector(field.X, field.Y, field.Z);
|
return FVector(field.X, field.Y, field.Z);
|
||||||
}
|
}
|
||||||
|
|
||||||
double UlxAnimationStepLibrary::AnimationStepGetFloat(const FlxAnimationStep& step, const FString& name) {
|
double UlxAnimationStepLibrary::AnimationStepGetFloat(const FlxAnimationStep& step, const FString& name) {
|
||||||
FlxAnimationField field = FindAnimationField(step, name);
|
FlxAnimationField field = FindAnimationField(step, name);
|
||||||
if (field.Type != ElxAnimValueType::NUMBER) return 0.0;
|
if (field.Type != SimpleDynamicTag::NUMBER) return 0.0;
|
||||||
return field.X;
|
return field.X;
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma optimize("", off)
|
|
||||||
FString UlxAnimationStepLibrary::AnimationStepGetString(const FlxAnimationStep& step, const FString& name) {
|
FString UlxAnimationStepLibrary::AnimationStepGetString(const FlxAnimationStep& step, const FString& name) {
|
||||||
|
if (step.Finished && (name == TEXT("action"))) {
|
||||||
|
return TEXT("idle");
|
||||||
|
}
|
||||||
FlxAnimationField field = FindAnimationField(step, name);
|
FlxAnimationField field = FindAnimationField(step, name);
|
||||||
if (field.Type != ElxAnimValueType::STRING) return TEXT("");
|
if (field.Type != SimpleDynamicTag::STRING) return TEXT("");
|
||||||
return FString(field.S.size(), (const UTF8CHAR*)(field.S.data()));
|
return FString(field.S.size(), (const UTF8CHAR*)(field.S.data()));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool UlxAnimationStepLibrary::AnimationStepGetBool(const FlxAnimationStep& step, const FString& name) {
|
bool UlxAnimationStepLibrary::AnimationStepGetBool(const FlxAnimationStep& step, const FString& name) {
|
||||||
FlxAnimationField field = FindAnimationField(step, name);
|
FlxAnimationField field = FindAnimationField(step, name);
|
||||||
if (field.Type != ElxAnimValueType::BOOLEAN) return false;
|
if (field.Type != SimpleDynamicTag::BOOLEAN) return false;
|
||||||
return field.X == 1.0;
|
return field.X == 1.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
FlxAnimationField FlxAnimationStepDecoder::ReadField() {
|
||||||
|
FlxAnimationField result;
|
||||||
|
result.Name = Decoder.read_string_view();
|
||||||
|
result.Persistent = Decoder.read_bool();
|
||||||
|
result.Type = (SimpleDynamicTag)Decoder.read_uint8();
|
||||||
|
switch (result.Type) {
|
||||||
|
case SimpleDynamicTag::STRING: {
|
||||||
|
result.S = Decoder.read_string_view();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SimpleDynamicTag::NUMBER: {
|
||||||
|
result.X = Decoder.read_double();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SimpleDynamicTag::BOOLEAN: {
|
||||||
|
result.X = Decoder.read_bool() ? 1.0 : 0.0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SimpleDynamicTag::VECTOR: {
|
||||||
|
result.X = Decoder.read_double();
|
||||||
|
result.Y = Decoder.read_double();
|
||||||
|
result.Z = Decoder.read_double();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
Decoder.read_bytes(Decoder.fill());
|
||||||
|
result.Type = SimpleDynamicTag::UNINITIALIZED;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
FString FlxAnimationStepDecoder::DebugString(bool injectidle, bool persistentonly, uint64 hash, std::string_view body) {
|
||||||
|
FString result;
|
||||||
|
FlxAnimationStepDecoder decoder(body);
|
||||||
|
result.Appendf(TEXT("Hash=%016llx"), hash);
|
||||||
|
if (injectidle) {
|
||||||
|
result.Appendf(TEXT(" action=idle"));
|
||||||
|
}
|
||||||
|
while (!decoder.AtEOF()) {
|
||||||
|
FlxAnimationField field = decoder.ReadField();
|
||||||
|
if (persistentonly && !field.Persistent) continue;
|
||||||
|
result.Append(TEXT(" "));
|
||||||
|
result.Append(FString(field.Name.size(), (const UTF8CHAR*)field.Name.data()));
|
||||||
|
result.Append(field.Persistent ? TEXT("=") : TEXT(":"));
|
||||||
|
switch (field.Type) {
|
||||||
|
case SimpleDynamicTag::STRING:
|
||||||
|
result.Append(FString(field.S.size(), (const UTF8CHAR*)field.S.data()));
|
||||||
|
break;
|
||||||
|
case SimpleDynamicTag::NUMBER:
|
||||||
|
result.Appendf(TEXT("%lf"), field.X);
|
||||||
|
break;
|
||||||
|
case SimpleDynamicTag::BOOLEAN:
|
||||||
|
result.Append((field.X) == 1.0 ? TEXT("true") : TEXT("false"));
|
||||||
|
break;
|
||||||
|
case SimpleDynamicTag::VECTOR:
|
||||||
|
result.Appendf(TEXT("%lf,%lf,%lf"), field.X, field.Y, field.Z);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
FlxAnimationStepView FlxAnimQueueDecoder::ReadStep() {
|
FlxAnimationStepView FlxAnimQueueDecoder::ReadStep() {
|
||||||
FlxAnimationStepView result;
|
FlxAnimationStepView result;
|
||||||
result.Hash = Decoder.read_uint64();
|
result.Hash = Decoder.read_uint64();
|
||||||
@@ -138,65 +249,16 @@ FlxAnimationStepView FlxAnimQueueDecoder::ReadStep() {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
FlxAnimationField FlxAnimationStepDecoder::ReadField() {
|
uint64 FlxAnimQueueDecoder::PeekHash() {
|
||||||
FlxAnimationField result;
|
int64_t read_count = Decoder.total_reads();
|
||||||
result.Name = Decoder.read_string_view();
|
uint64 result = Decoder.read_uint64();
|
||||||
result.Persistent = Decoder.read_bool();
|
Decoder.unread_to(read_count);
|
||||||
result.Type = (ElxAnimValueType)Decoder.read_uint8();
|
|
||||||
switch (result.Type) {
|
|
||||||
case ElxAnimValueType::STRING: {
|
|
||||||
result.S = Decoder.read_string_view();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case ElxAnimValueType::NUMBER: {
|
|
||||||
result.X = Decoder.read_double();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case ElxAnimValueType::BOOLEAN: {
|
|
||||||
result.X = Decoder.read_bool() ? 1.0 : 0.0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case ElxAnimValueType::XYZ: {
|
|
||||||
result.X = Decoder.read_double();
|
|
||||||
result.Y = Decoder.read_double();
|
|
||||||
result.Z = Decoder.read_double();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
Decoder.set_at_eof();
|
|
||||||
result.Type = ElxAnimValueType::BOOLEAN;
|
|
||||||
result.X = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
FString FlxAnimationStepDecoder::DebugString(uint64 hash, std::string_view body) {
|
FlxAnimQueueDecoder::FlxAnimQueueDecoder(std::string_view queue) : Decoder(queue) {
|
||||||
FString result;
|
SizeLimit = Decoder.read_uint8();
|
||||||
FlxAnimationStepDecoder decoder(body);
|
ActualSize = Decoder.read_uint8();
|
||||||
result.Appendf(TEXT("Hash=%016llx"), hash);
|
|
||||||
while (!decoder.AtEOF()) {
|
|
||||||
FlxAnimationField field = decoder.ReadField();
|
|
||||||
result.Append(TEXT(" "));
|
|
||||||
result.Append(FString(field.Name.size(), (const UTF8CHAR*)field.Name.data()));
|
|
||||||
result.Append(field.Persistent ? TEXT("=") : TEXT(":"));
|
|
||||||
switch (field.Type) {
|
|
||||||
case ElxAnimValueType::STRING:
|
|
||||||
result.Append(FString(field.S.size(), (const UTF8CHAR*)field.S.data()));
|
|
||||||
break;
|
|
||||||
case ElxAnimValueType::NUMBER:
|
|
||||||
result.Appendf(TEXT("%lf"), field.X);
|
|
||||||
break;
|
|
||||||
case ElxAnimValueType::BOOLEAN:
|
|
||||||
result.Append((field.X) == 1.0 ? TEXT("true") : TEXT("false"));
|
|
||||||
break;
|
|
||||||
case ElxAnimValueType::XYZ:
|
|
||||||
result.Appendf(TEXT("%lf,%lf,%lf"), field.X, field.Y, field.Z);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FString FlxAnimQueueDecoder::DebugString(std::string_view queue) {
|
FString FlxAnimQueueDecoder::DebugString(std::string_view queue) {
|
||||||
@@ -204,42 +266,62 @@ FString FlxAnimQueueDecoder::DebugString(std::string_view queue) {
|
|||||||
FlxAnimQueueDecoder decoder(queue);
|
FlxAnimQueueDecoder decoder(queue);
|
||||||
while (!decoder.AtEOF()) {
|
while (!decoder.AtEOF()) {
|
||||||
FlxAnimationStepView step = decoder.ReadStep();
|
FlxAnimationStepView step = decoder.ReadStep();
|
||||||
FString stepdebug = FlxAnimationStepDecoder::DebugString(step.Hash, step.Body);
|
FString stepdebug = FlxAnimationStepDecoder::DebugString(false, false, step.Hash, step.Body);
|
||||||
result.Appendf(TEXT("%s\n"), *stepdebug);
|
result.Appendf(TEXT("%s\n"), *stepdebug);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
FlxAnimTracker::FlxAnimTracker() {
|
FlxAnimTracker::FlxAnimTracker() {
|
||||||
|
Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlxAnimTracker::Clear() {
|
||||||
AQ.Empty();
|
AQ.Empty();
|
||||||
FirstSeqno = 0;
|
Changed = true;
|
||||||
HashToSeqno.Empty();
|
}
|
||||||
UnstartedSeqno = 0;
|
|
||||||
PlaybackMode = ElxAnimationMode::INVALID;
|
void FlxAnimTracker::FinishedAnimation(uint64 hash) {
|
||||||
AbortedHashes.Empty();
|
for (int i = 0; i < AQ.Num(); i++) {
|
||||||
|
if (AQ[i].Hash == hash) {
|
||||||
|
AQ[i].Finished = true;
|
||||||
|
Changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlxAnimTracker::SkipToEnd() {
|
||||||
|
for (int i = 0; i < AQ.Num(); i++) {
|
||||||
|
if (!AQ[i].Finished) {
|
||||||
|
AQ[i].Finished = true;
|
||||||
|
Changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FlxAnimTracker::Update(std::string_view encqueue) {
|
void FlxAnimTracker::Update(std::string_view encqueue) {
|
||||||
// Check for an exact match. If the most recent entry
|
check(!encqueue.empty());
|
||||||
// in encqueue has the same hash as the most recent
|
|
||||||
// entry in AQ, then that's an exact match. Or,
|
// If the first hash matches, we don't bother updating at all.
|
||||||
// if both are empty, that's also an exact match.
|
|
||||||
// In case of exact match, abort early, there's no
|
|
||||||
// further work needed.
|
|
||||||
//
|
//
|
||||||
if (encqueue.empty()) {
|
FlxAnimQueueDecoder decoder(encqueue);
|
||||||
if (AQ.IsEmpty()) {
|
if (!AQ.IsEmpty()) {
|
||||||
|
if (decoder.PeekHash() == AQ.Last().Hash) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
if (!AQ.IsEmpty()) {
|
|
||||||
FlxStringDecoder qdecoder(encqueue);
|
|
||||||
uint64 hash = qdecoder.read_uint64();
|
// The Changed flag is set whenever there is any change to
|
||||||
if (hash == AQ.Last().Hash) {
|
// the animation queue of any kind.
|
||||||
return;
|
//
|
||||||
}
|
Changed = true;
|
||||||
}
|
|
||||||
|
// Build a map indexing the steps in AQ.
|
||||||
|
//
|
||||||
|
TMap<uint64, int32> HashToIndex;
|
||||||
|
for (int i = 0; i < AQ.Num(); i++) {
|
||||||
|
HashToIndex.Emplace(AQ[i].Hash, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Search for a Luprex animation record that matches
|
// Search for a Luprex animation record that matches
|
||||||
@@ -249,108 +331,59 @@ void FlxAnimTracker::Update(std::string_view encqueue) {
|
|||||||
// At the same time, push all non-matching records
|
// At the same time, push all non-matching records
|
||||||
// after the matching record onto a stack of new records.
|
// after the matching record onto a stack of new records.
|
||||||
//
|
//
|
||||||
FlxAnimQueueDecoder decoder(encqueue);
|
|
||||||
TArray<FlxAnimationStepView> newsteps;
|
TArray<FlxAnimationStepView> newsteps;
|
||||||
int32 matchingseqno = -1;
|
int32 matchingindex = -1;
|
||||||
while (!decoder.AtEOF()) {
|
while (!decoder.AtEOF()) {
|
||||||
FlxAnimationStepView step = decoder.ReadStep();
|
FlxAnimationStepView step = decoder.ReadStep();
|
||||||
int32* stepseq = HashToSeqno.Find(step.Hash);
|
int32* indexp = HashToIndex.Find(step.Hash);
|
||||||
if (stepseq == nullptr) {
|
if (indexp == nullptr) {
|
||||||
newsteps.Emplace(step);
|
newsteps.Emplace(step);
|
||||||
} else {
|
} else {
|
||||||
matchingseqno = *stepseq;
|
matchingindex = *indexp;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove all animations after the most recent matching
|
// Remove all animations after the most recent matching
|
||||||
// record. If we remove a 'started' animation, add that
|
// record.
|
||||||
// animation to the list of aborted animations.
|
|
||||||
//
|
//
|
||||||
int32 nremove = (FirstSeqno + AQ.Num()) - (matchingseqno + 1);
|
int32 nremove = (AQ.Num() - (matchingindex + 1));
|
||||||
check((nremove >= 0) && (nremove <= AQ.Num()));
|
check((nremove >= 0) && (nremove <= AQ.Num()));
|
||||||
for (int32 i = 0; i < nremove; i++) {
|
for (int32 i = 0; i < nremove; i++) {
|
||||||
uint64 lasthash = AQ.Last().Hash;
|
|
||||||
int32 seqno = FirstSeqno + AQ.Num() - 1;
|
|
||||||
HashToSeqno.Remove(lasthash);
|
|
||||||
if (seqno < UnstartedSeqno) {
|
|
||||||
AbortedHashes.Emplace(lasthash);
|
|
||||||
}
|
|
||||||
AQ.PopLast();
|
AQ.PopLast();
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we aborted a 'started' animation, we have to fix
|
|
||||||
// up the Unstarted animation pointer.
|
|
||||||
//
|
|
||||||
// Note: this could leave the Unstarted pointer at
|
|
||||||
// seqno less than FirstSeqno. We will fix that state
|
|
||||||
// of affairs up later.
|
|
||||||
//
|
|
||||||
if (UnstartedSeqno > (FirstSeqno + AQ.Num())) {
|
|
||||||
UnstartedSeqno = matchingseqno;
|
|
||||||
PlaybackMode = ElxAnimationMode::BlendToFinal;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Transfer the new animations onto the queue.
|
// Transfer the new animations onto the queue.
|
||||||
//
|
//
|
||||||
while (!newsteps.IsEmpty()) {
|
while (!newsteps.IsEmpty()) {
|
||||||
FlxAnimationStepView step = newsteps.Pop();
|
FlxAnimationStepView step = newsteps.Pop();
|
||||||
int32 seqno = FirstSeqno + AQ.Num();
|
|
||||||
AQ.EmplaceLast(step.Hash, step.Body);
|
AQ.EmplaceLast(step.Hash, step.Body);
|
||||||
HashToSeqno.Emplace(step.Hash, seqno);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there are too many animations in AQ, discard
|
// If there are too many animations in AQ, discard
|
||||||
// any very old ones.
|
// any very old ones.
|
||||||
//
|
//
|
||||||
if (AQ.Num() > 10) {
|
// TODO: this is hardwired to keep 10. Instead, we
|
||||||
int ndiscard = AQ.Num() - 10;
|
// should keep the number specified in the queue.
|
||||||
for (int i = 0; i < ndiscard; i++) {
|
|
||||||
uint64 hash = AQ.First().Hash;
|
|
||||||
HashToSeqno.Remove(hash);
|
|
||||||
AQ.PopFirst();
|
|
||||||
FirstSeqno += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If UnstartedSeqno is before the live range,
|
|
||||||
// then the whole queue has to be replayed. Don't
|
|
||||||
// do that: just fast skip to the end.
|
|
||||||
//
|
//
|
||||||
if (UnstartedSeqno <= FirstSeqno) {
|
int limit = 10;
|
||||||
if (AQ.Num() == 0) {
|
int ndiscard = AQ.Num() - limit;
|
||||||
UnstartedSeqno = FirstSeqno;
|
if (ndiscard > 0) {
|
||||||
} else {
|
for (int i = 0; i < ndiscard; i++) {
|
||||||
UnstartedSeqno = FirstSeqno + AQ.Num() - 1;
|
AQ.PopFirst();
|
||||||
}
|
}
|
||||||
PlaybackMode = ElxAnimationMode::WarpToFinal;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TArray<uint64> FlxAnimTracker::GetAborted() {
|
FlxAnimationStep FlxAnimTracker::GetCurrentAnimation() {
|
||||||
TArray<uint64> result;
|
FlxAnimationStep result;
|
||||||
result = std::move(AbortedHashes);
|
for (int i = 0; i < AQ.Num(); i++) {
|
||||||
AbortedHashes.Empty();
|
if (!AQ[i].Finished) {
|
||||||
|
return AQ[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result = AQ.Last();
|
||||||
|
result.Hash = 0;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
ElxAnimationMode FlxAnimTracker::GetNextStep(FlxAnimationStep &step) {
|
|
||||||
int offset = UnstartedSeqno - FirstSeqno;
|
|
||||||
if (offset < AQ.Num()) {
|
|
||||||
step = AQ[offset];
|
|
||||||
return PlaybackMode;
|
|
||||||
} else {
|
|
||||||
step.Hash = 0;
|
|
||||||
step.Body.Empty();
|
|
||||||
return ElxAnimationMode::INVALID;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FlxAnimTracker::StartedStep(uint64 hash) {
|
|
||||||
int offset = UnstartedSeqno - FirstSeqno;
|
|
||||||
check(offset < AQ.Num());
|
|
||||||
check(AQ[offset].Hash == hash);
|
|
||||||
UnstartedSeqno += 1;
|
|
||||||
PlaybackMode = ElxAnimationMode::StartAnimation;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
@@ -5,46 +5,6 @@
|
|||||||
#include "Containers/Deque.h"
|
#include "Containers/Deque.h"
|
||||||
#include "AnimQueue.generated.h"
|
#include "AnimQueue.generated.h"
|
||||||
|
|
||||||
////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// This is copied over from Luprex source. Not ideal.
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////
|
|
||||||
|
|
||||||
enum class ElxAnimValueType {
|
|
||||||
STRING,
|
|
||||||
NUMBER,
|
|
||||||
BOOLEAN,
|
|
||||||
XYZ,
|
|
||||||
INVALID
|
|
||||||
};
|
|
||||||
|
|
||||||
////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// Playback Modes
|
|
||||||
//
|
|
||||||
// There are three different ways to play an animation:
|
|
||||||
//
|
|
||||||
// 1. Start Animation. This is the normal way to play an animation.
|
|
||||||
//
|
|
||||||
// 2. Warp to Final. Skip the actual animation, and jump
|
|
||||||
// instantaneously to the final state of the animation.
|
|
||||||
//
|
|
||||||
// 3. Blend to Final. Skip the actual animation, and blend
|
|
||||||
// smoothly to the final state of the animation. If the current
|
|
||||||
// state is very far from the final state, then maybe
|
|
||||||
// jump instantaneously instead.
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////
|
|
||||||
|
|
||||||
UENUM(BlueprintType)
|
|
||||||
enum class ElxAnimationMode : uint8 {
|
|
||||||
StartAnimation,
|
|
||||||
WarpToFinal,
|
|
||||||
BlendToFinal,
|
|
||||||
INVALID,
|
|
||||||
};
|
|
||||||
|
|
||||||
////////////////////////////////////////////////
|
////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// An single animation step.
|
// An single animation step.
|
||||||
@@ -61,13 +21,19 @@ struct INTEGRATION_API FlxAnimationStep {
|
|||||||
GENERATED_BODY()
|
GENERATED_BODY()
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
UPROPERTY()
|
||||||
|
bool Finished;
|
||||||
|
|
||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
uint64 Hash;
|
uint64 Hash;
|
||||||
|
|
||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
TArray<uint8> Body;
|
TArray<uint8> Body;
|
||||||
|
|
||||||
FlxAnimationStep() : Hash(0), Body() {}
|
UPROPERTY()
|
||||||
|
FString Blueprint;
|
||||||
|
|
||||||
|
FlxAnimationStep() : Finished(false), Hash(0), Body(), Blueprint() {}
|
||||||
FlxAnimationStep(uint64 h, std::string_view b);
|
FlxAnimationStep(uint64 h, std::string_view b);
|
||||||
|
|
||||||
// Unpack an AnimStep into a UObject
|
// Unpack an AnimStep into a UObject
|
||||||
@@ -78,6 +44,14 @@ public:
|
|||||||
// routine tries to find a string property "color" in the
|
// routine tries to find a string property "color" in the
|
||||||
// UObject, and then it sets that property to "blue."
|
// UObject, and then it sets that property to "blue."
|
||||||
//
|
//
|
||||||
|
// The prefix is prepended to the key names. For example,
|
||||||
|
// if one of the key-value pairs is "color=blue", and the
|
||||||
|
// prefix is "aq", then the property "aqColor=blue" will be
|
||||||
|
// stored in the UObject.
|
||||||
|
//
|
||||||
|
// All properties of the UObject starting with the specified
|
||||||
|
// prefix are cleared before unpacking the animation step.
|
||||||
|
//
|
||||||
// Returns true if all of the key-value pairs in the
|
// Returns true if all of the key-value pairs in the
|
||||||
// animation step could be unpacked into fields of the UObject.
|
// animation step could be unpacked into fields of the UObject.
|
||||||
// This could fail, for instance, if the UObject just doesn't
|
// This could fail, for instance, if the UObject just doesn't
|
||||||
@@ -85,16 +59,23 @@ public:
|
|||||||
// fail if there's a type mismatch. For example, "color=blue"
|
// fail if there's a type mismatch. For example, "color=blue"
|
||||||
// cannot be stored in a property "color" of type int.
|
// cannot be stored in a property "color" of type int.
|
||||||
//
|
//
|
||||||
// The prefix is prepended to the key names. For example,
|
// Automatically injects a boolean property "idle" representing
|
||||||
// if one of the key-value pairs is "color=blue", and the
|
// whether the property's finished flag is set.
|
||||||
// prefix is "aq", then the property "aqColor=blue" will be
|
|
||||||
// stored in the UObject.
|
|
||||||
//
|
//
|
||||||
// If pre-clear is true, then all properties of the UObject
|
bool Unpack(const FString& prefix, UObject* into) const;
|
||||||
// starting with the specified prefix are cleared before
|
|
||||||
// unpacking the animation step.
|
// Auto-Execute
|
||||||
//
|
//
|
||||||
bool Unpack(const FString& prefix, UObject* into, bool preclear = true) const;
|
// These functions automatically update certain actor
|
||||||
|
// properties:
|
||||||
|
//
|
||||||
|
// AutoUpdateXYZ(AActor *actor); // uses 'xyz' keyword
|
||||||
|
// AutoUpdateFacing(AActor *actor); // uses 'facing' keyword.
|
||||||
|
// AutoUpdatePlane(FName *plane); // uses 'plane' keyword
|
||||||
|
//
|
||||||
|
void AutoUpdateXYZ(AActor *actor) const;
|
||||||
|
void AutoUpdateFacing(AActor *actor) const;
|
||||||
|
void AutoUpdatePlane(FName *plane) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
////////////////////////////////////////////////
|
////////////////////////////////////////////////
|
||||||
@@ -114,9 +95,14 @@ public:
|
|||||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category = Luprex)
|
UFUNCTION(BlueprintCallable, BlueprintPure, Category = Luprex)
|
||||||
static FString AnimationStepDebugString(const FlxAnimationStep& step);
|
static FString AnimationStepDebugString(const FlxAnimationStep& step);
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable, Category = Luprex)
|
UFUNCTION(BlueprintCallable, Meta = (DefaultToSelf = "into"), Category = Luprex)
|
||||||
static void UnpackAnimationStep(const FlxAnimationStep& step,
|
static void UnpackAnimationStep(UObject* into, const FlxAnimationStep& step, const FString& VariableNamePrefix = TEXT("aq"));
|
||||||
const FString& VariableNamePrefix, UObject* into);
|
|
||||||
|
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Luprex")
|
||||||
|
static bool AnimationStepEqual(const FlxAnimationStep &StepA, const FlxAnimationStep &StepB);
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Luprex")
|
||||||
|
static bool AnimationStepIsIdle(const FlxAnimationStep &step);
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Luprex")
|
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Luprex")
|
||||||
static FVector AnimationStepGetVector(const FlxAnimationStep& step, const FString& name);
|
static FVector AnimationStepGetVector(const FlxAnimationStep& step, const FString& name);
|
||||||
@@ -129,18 +115,13 @@ public:
|
|||||||
|
|
||||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Luprex")
|
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Luprex")
|
||||||
static bool AnimationStepGetBool(const FlxAnimationStep& step, const FString& name);
|
static bool AnimationStepGetBool(const FlxAnimationStep& step, const FString& name);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
////////////////////////////////////////////////
|
////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Exposing functions to blueprints.
|
|
||||||
//
|
//
|
||||||
////////////////////////////////////////////////
|
////////////////////////////////////////////////
|
||||||
|
|
||||||
//UFUNCTION(BlueprintCallable)
|
|
||||||
//void Unpack(const FString& prefix, UObject* into, bool preclear = true);
|
|
||||||
|
|
||||||
struct FlxAnimationStepView {
|
struct FlxAnimationStepView {
|
||||||
uint64 Hash;
|
uint64 Hash;
|
||||||
std::string_view Body;
|
std::string_view Body;
|
||||||
@@ -166,7 +147,7 @@ struct FlxAnimationStepView {
|
|||||||
struct FlxAnimationField {
|
struct FlxAnimationField {
|
||||||
std::string_view Name;
|
std::string_view Name;
|
||||||
bool Persistent;
|
bool Persistent;
|
||||||
ElxAnimValueType Type;
|
SimpleDynamicTag Type;
|
||||||
double X, Y, Z;
|
double X, Y, Z;
|
||||||
std::string_view S;
|
std::string_view S;
|
||||||
};
|
};
|
||||||
@@ -184,21 +165,38 @@ struct FlxAnimationField {
|
|||||||
|
|
||||||
class FlxAnimQueueDecoder {
|
class FlxAnimQueueDecoder {
|
||||||
private:
|
private:
|
||||||
FlxStringDecoder Decoder;
|
FlxStreamBuffer Decoder;
|
||||||
|
|
||||||
|
// These values are immediately read from the header.
|
||||||
|
//
|
||||||
|
int SizeLimit;
|
||||||
|
int ActualSize;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Initialize the FlxAnimQueueDecoder with the encoded animation queue.
|
// Initialize the FlxAnimQueueDecoder with the encoded animation queue.
|
||||||
//
|
//
|
||||||
FlxAnimQueueDecoder(std::string_view s) : Decoder(s) {}
|
FlxAnimQueueDecoder(std::string_view s);
|
||||||
|
|
||||||
|
// Get the size limit of the animation queue.
|
||||||
|
//
|
||||||
|
int GetSizeLimit() const { return SizeLimit; }
|
||||||
|
|
||||||
|
// Get the Actual Size of the animation queue.
|
||||||
|
//
|
||||||
|
int GetActualSize() const { return ActualSize; }
|
||||||
|
|
||||||
// Return true if the parser has reached the end of the string.
|
// Return true if the parser has reached the end of the string.
|
||||||
//
|
//
|
||||||
bool AtEOF() { return Decoder.at_eof(); }
|
bool AtEOF() { return Decoder.empty(); }
|
||||||
|
|
||||||
// Read one animation step.
|
// Read one animation step.
|
||||||
//
|
//
|
||||||
FlxAnimationStepView ReadStep();
|
FlxAnimationStepView ReadStep();
|
||||||
|
|
||||||
|
// Peek at the hash of the next animation step.
|
||||||
|
//
|
||||||
|
uint64 PeekHash();
|
||||||
|
|
||||||
// Convert an AnimQueue to an FString.
|
// Convert an AnimQueue to an FString.
|
||||||
//
|
//
|
||||||
static FString DebugString(std::string_view s);
|
static FString DebugString(std::string_view s);
|
||||||
@@ -217,7 +215,7 @@ public:
|
|||||||
|
|
||||||
class FlxAnimationStepDecoder {
|
class FlxAnimationStepDecoder {
|
||||||
private:
|
private:
|
||||||
FlxStringDecoder Decoder;
|
FlxStreamBuffer Decoder;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Initialize the FlxAnimationStepDecoder from the FlxAnimationStepView.
|
// Initialize the FlxAnimationStepDecoder from the FlxAnimationStepView.
|
||||||
@@ -226,7 +224,7 @@ public:
|
|||||||
|
|
||||||
// Return true if the parser has reached the end of the string.
|
// Return true if the parser has reached the end of the string.
|
||||||
//
|
//
|
||||||
bool AtEOF() { return Decoder.at_eof(); }
|
bool AtEOF() { return Decoder.empty(); }
|
||||||
|
|
||||||
// Read one field.
|
// Read one field.
|
||||||
//
|
//
|
||||||
@@ -234,7 +232,7 @@ public:
|
|||||||
|
|
||||||
// Convert an AnimStep to an FString.
|
// Convert an AnimStep to an FString.
|
||||||
//
|
//
|
||||||
static FString DebugString(uint64 hash, std::string_view body);
|
static FString DebugString(bool injectidle, bool persistentonly, uint64 hash, std::string_view body);
|
||||||
};
|
};
|
||||||
|
|
||||||
////////////////////////////////////////////////
|
////////////////////////////////////////////////
|
||||||
@@ -257,26 +255,11 @@ public:
|
|||||||
//
|
//
|
||||||
TDeque<FlxAnimationStep> AQ;
|
TDeque<FlxAnimationStep> AQ;
|
||||||
|
|
||||||
// The sequence number of the first item in AQ.
|
// True if something has recently changed.
|
||||||
//
|
//
|
||||||
int32 FirstSeqno;
|
bool Changed;
|
||||||
|
|
||||||
// Map from hash to sequence number.
|
|
||||||
//
|
|
||||||
TMap<uint64, int32> HashToSeqno;
|
|
||||||
|
|
||||||
// The sequence number of the first unstarted animation.
|
|
||||||
//
|
|
||||||
int32 UnstartedSeqno;
|
|
||||||
|
|
||||||
// Indicates whether the unstarted animation should be played or otherwise.
|
|
||||||
//
|
|
||||||
ElxAnimationMode PlaybackMode;
|
|
||||||
|
|
||||||
// Array of recently-aborted hash values.
|
|
||||||
//
|
|
||||||
TArray<uint64> AbortedHashes;
|
|
||||||
|
|
||||||
|
private:
|
||||||
public:
|
public:
|
||||||
// Construct a tracker.
|
// Construct a tracker.
|
||||||
//
|
//
|
||||||
@@ -284,6 +267,10 @@ public:
|
|||||||
//
|
//
|
||||||
FlxAnimTracker();
|
FlxAnimTracker();
|
||||||
|
|
||||||
|
// Clear everything, reset to the initial state.
|
||||||
|
//
|
||||||
|
void Clear();
|
||||||
|
|
||||||
// Update from the specified animation queue.
|
// Update from the specified animation queue.
|
||||||
//
|
//
|
||||||
// After the update is done, AQ will be a copy
|
// After the update is done, AQ will be a copy
|
||||||
@@ -291,24 +278,39 @@ public:
|
|||||||
//
|
//
|
||||||
void Update(std::string_view encqueue);
|
void Update(std::string_view encqueue);
|
||||||
|
|
||||||
// Fetch the aborted hash values.
|
// Get the current animation step.
|
||||||
//
|
//
|
||||||
// This gets the array of aborted hashes and clears
|
// Get the current animation step. This is the step that the
|
||||||
// the stored array.
|
// blueprint should currently be playing.
|
||||||
//
|
//
|
||||||
TArray<uint64> GetAborted();
|
FlxAnimationStep GetCurrentAnimation();
|
||||||
|
|
||||||
// Get the next unstarted animation step.
|
// Declare that an animation is finished.
|
||||||
//
|
//
|
||||||
// Get the next animation step. Returns the next step and the
|
// The blueprint uses this function call to indicate that it
|
||||||
// playback mode. If the playback mode is INVALID then there is
|
// is done playing the specified animation. This will cause the
|
||||||
// no next step to play
|
// animation to be marked as finished, which in turn causes
|
||||||
|
// 'GetCurrentStep' to advance to the next animation.
|
||||||
//
|
//
|
||||||
ElxAnimationMode GetNextStep(FlxAnimationStep& step);
|
void FinishedAnimation(uint64 Hash);
|
||||||
|
|
||||||
// Declare that an animation has been started.
|
// Skip to the end of the animation queue.
|
||||||
//
|
//
|
||||||
// After starting an animation, you should call this.
|
// This is equivalent to calling 'FinishedHash' on every
|
||||||
|
// animation in the entire queue.
|
||||||
//
|
//
|
||||||
void StartedStep(uint64 Hash);
|
void SkipToEnd();
|
||||||
|
|
||||||
|
// Clear the 'Changed' flag.
|
||||||
|
//
|
||||||
|
void ClearChanged() { Changed = false; }
|
||||||
|
|
||||||
|
// Get the 'Changed' flag.
|
||||||
|
//
|
||||||
|
// The changed flag is set to true whenever the Luprex animation
|
||||||
|
// queue changes from its previous state. The changed flag is also
|
||||||
|
// set to true whenever 'SetFinished' marks an animation as finished.
|
||||||
|
// The changed flag can only be set to false by 'ClearChanged,' above.
|
||||||
|
//
|
||||||
|
bool IsChanged() const { return Changed; }
|
||||||
};
|
};
|
||||||
@@ -2,10 +2,10 @@
|
|||||||
|
|
||||||
#include "IntegrationGameModeBase.h"
|
#include "IntegrationGameModeBase.h"
|
||||||
#include "lpx-drvutil.hpp"
|
#include "lpx-drvutil.hpp"
|
||||||
|
#include "lpx-paths.hpp"
|
||||||
#include "DebugPrint.h"
|
#include "DebugPrint.h"
|
||||||
#include "Tangible.h"
|
#include "Tangible.h"
|
||||||
#include "TangibleManager.h"
|
#include "TangibleManager.h"
|
||||||
#include "TangibleInterface.h"
|
|
||||||
#include "CommonTypes.h"
|
#include "CommonTypes.h"
|
||||||
#include "AnimQueue.h"
|
#include "AnimQueue.h"
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -16,6 +16,7 @@ using namespace CommonTypes;
|
|||||||
|
|
||||||
AIntegrationGameModeBase::AIntegrationGameModeBase()
|
AIntegrationGameModeBase::AIntegrationGameModeBase()
|
||||||
{
|
{
|
||||||
|
TangibleManager = NewObject<UlxTangibleManager>();
|
||||||
EngineSeconds = 0.0;
|
EngineSeconds = 0.0;
|
||||||
NextThreadTrigger = 1.0;
|
NextThreadTrigger = 1.0;
|
||||||
//PrimaryActorTick.bCanEverTick = true; // Probably wrong
|
//PrimaryActorTick.bCanEverTick = true; // Probably wrong
|
||||||
@@ -47,6 +48,11 @@ void AIntegrationGameModeBase::ResetToInitialState()
|
|||||||
{
|
{
|
||||||
Playing = false;
|
Playing = false;
|
||||||
|
|
||||||
|
if (TangibleManager != nullptr) {
|
||||||
|
TangibleManager->ConditionalBeginDestroy();
|
||||||
|
TangibleManager = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
// Shut down the thread
|
// Shut down the thread
|
||||||
LuprexUpdateTask.Shutdown();
|
LuprexUpdateTask.Shutdown();
|
||||||
|
|
||||||
@@ -104,67 +110,73 @@ void AIntegrationGameModeBase::MaybeTriggerUpdateTask(float deltaseconds) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//#pragma optimize( "", off )
|
|
||||||
//void SetLocal(UObject* obj, const char *name, int value) {
|
|
||||||
// FString sname((const UTF8CHAR *)name);
|
#pragma optimize("", off)
|
||||||
// FName nname(sname);
|
|
||||||
// UClass* uclass = obj->GetClass();
|
|
||||||
// FProperty* fprop = FindFProperty<FProperty>(uclass, nname);
|
|
||||||
// FStructProperty* sprop = FindFProperty<FStructProperty>(uclass, nname);
|
|
||||||
//}
|
|
||||||
|
|
||||||
void AIntegrationGameModeBase::UpdateTangibles() {
|
void AIntegrationGameModeBase::UpdateTangibles() {
|
||||||
|
double radius = 1000.0; // Hardwired for now.
|
||||||
|
using TanArray = UlxTangibleManager::TanArray;
|
||||||
if (!Playing) return;
|
if (!Playing) return;
|
||||||
FlxLockedWrapper w(LockableWrapper);
|
FlxLockedWrapper w(LockableWrapper);
|
||||||
int64 actor = w.GetActor();
|
int64 player = w.GetActor();
|
||||||
TangibleManager.SetActor(actor);
|
IdView nearids = w.GetNear(player, radius, radius, radius);
|
||||||
TangibleManager.SetNear(w.GetNear(actor, 100, 100, 100));
|
TangibleManager->UpdateNearAccordingToLuprex(nearids);
|
||||||
for (int64 id : TangibleManager.GetNear()) {
|
TanArray alltans = TangibleManager->GetAllTangibles();
|
||||||
TangibleManager.MakeTangible(id);
|
IdArray allids = TangibleManager->GetIds(alltans);
|
||||||
|
StringViewVec allqueues = w.GetAnimationQueues(allids);
|
||||||
|
for (int i = 0; i < alltans.Num(); i++) {
|
||||||
|
alltans[i]->UpdateAnimationQueue(allqueues[i]);
|
||||||
}
|
}
|
||||||
// Update animation queues of live tangibles.
|
TangibleManager->RecalcNearAccordingToUnreal(player, radius);
|
||||||
IdArray tanids = TangibleManager.GetLive();
|
TangibleManager->DeleteFarawayTangibles();
|
||||||
StringViewVec aqueues = w.GetAnimationQueues(tanids);
|
}
|
||||||
for (int i = 0; i < tanids.Num(); i++) {
|
|
||||||
uint64_t tanid = tanids[i];
|
|
||||||
std::string_view aqueue = aqueues[i];
|
|
||||||
UlxTangible* t = TangibleManager.GetTangible(tanid);
|
|
||||||
check(t != nullptr);
|
|
||||||
t->AnimTracker.Update(aqueue);
|
|
||||||
|
|
||||||
TArray<uint64> aborted = t->AnimTracker.GetAborted();
|
void AIntegrationGameModeBase::ExecuteDebuggingCommand(const FString &fs) {
|
||||||
for (uint64 hash : aborted) {
|
if (fs == "\\invokeplayer") {
|
||||||
IlxTangibleInterface::Execute_AbortAnimation(t->Actor, hash);
|
DPrint(TEXT("Trying to invoke 'myfunction' in lua"));
|
||||||
}
|
FlxLockedWrapper w(LockableWrapper);
|
||||||
FlxAnimationStep step;
|
FlxStreamBuffer sb;
|
||||||
ElxAnimationMode mode = t->AnimTracker.GetNextStep(step);
|
sb.write_string("myfunction");
|
||||||
if (mode != ElxAnimationMode::INVALID) {
|
sb.write_simple_dynamic_tag(SimpleDynamicTag::NUMBER);
|
||||||
bool started = IlxTangibleInterface::Execute_StartAnimation(t->Actor, mode, step);
|
sb.write_double(3.0);
|
||||||
if (started) {
|
sb.write_simple_dynamic_tag(SimpleDynamicTag::STRING);
|
||||||
t->AnimTracker.StartedStep(step.Hash);
|
sb.write_string("Howdy");
|
||||||
}
|
sb.write_simple_dynamic_tag(SimpleDynamicTag::VECTOR);
|
||||||
}
|
sb.write_fvector(FVector(2,3,4));
|
||||||
|
std::string_view datapk = sb.view();
|
||||||
|
int64 player = w.GetActor();
|
||||||
|
w->play_invoke_engio(w.Get(), player, datapk.size(), datapk.data());
|
||||||
|
} else {
|
||||||
|
ConsoleOutput.AppendLine(TEXT("Unknown Command"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void AIntegrationGameModeBase::ConsoleSendInput(const FString& fs)
|
void AIntegrationGameModeBase::ConsoleSendInput(const FString& fs)
|
||||||
{
|
{
|
||||||
FlxLockedWrapper w(LockableWrapper);
|
FlxLockedWrapper w(LockableWrapper);
|
||||||
if (w->engine != nullptr)
|
if (w->engine != nullptr)
|
||||||
{
|
{
|
||||||
const TCHAR* fstchar = *fs;
|
ConsoleOutput.AppendLine(FString("> ") + fs);
|
||||||
if (sizeof(TCHAR) == 2)
|
// This is a bad way to do this. The problem is that if some
|
||||||
{
|
// lua code contains '\\', we'll catch it instead of passing it
|
||||||
ConsoleOutput.AppendLine(FString("> ") + fs);
|
// through. There's no simple solution, though.
|
||||||
std::u16string_view fsview((const char16_t*)fstchar, fs.Len());
|
if (fs[0] == '\\') {
|
||||||
std::string utf8 = drvutil::utf16_to_utf8(fsview);
|
ExecuteDebuggingCommand(fs);
|
||||||
utf8 = utf8 + "\n";
|
} else {
|
||||||
w->play_recv_incoming(w.Get(), 0, utf8.size(), utf8.c_str());
|
const TCHAR* fstchar = *fs;
|
||||||
|
if (sizeof(TCHAR) == 2)
|
||||||
|
{
|
||||||
|
std::u16string_view fsview((const char16_t*)fstchar, fs.Len());
|
||||||
|
std::string utf8 = drvutil::utf16_to_utf8(fsview);
|
||||||
|
utf8 = utf8 + "\n";
|
||||||
|
w->play_recv_incoming(w.Get(), 0, utf8.size(), utf8.c_str());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void AIntegrationGameModeBase::Tick(float deltaseconds)
|
void AIntegrationGameModeBase::Tick(float deltaseconds)
|
||||||
{
|
{
|
||||||
Super::Tick(deltaseconds);
|
Super::Tick(deltaseconds);
|
||||||
@@ -209,22 +221,25 @@ void AIntegrationGameModeBase::BeginPlay()
|
|||||||
if (w->play_initialize != nullptr)
|
if (w->play_initialize != nullptr)
|
||||||
{
|
{
|
||||||
drvutil::ostringstream srcpak;
|
drvutil::ostringstream srcpak;
|
||||||
std::string srcpakerr = drvutil::package_lua_source("c:\\Luprex", &srcpak);
|
std::string srcpakerr = drvutil::package_lua_source(LUPREX_ROOT_PATH, &srcpak);
|
||||||
if (!srcpakerr.empty())
|
if (!srcpakerr.empty())
|
||||||
{
|
{
|
||||||
DPrint(srcpakerr.c_str());
|
DPrint(srcpakerr.c_str());
|
||||||
}
|
}
|
||||||
std::string_view srcpakv = srcpak.view();
|
else
|
||||||
char* argv[1];
|
|
||||||
argv[0] = const_cast<char*>("lpxserver");
|
|
||||||
w->play_initialize(w.Get(), 1, argv, srcpakv.size(), srcpakv.data(), "");
|
|
||||||
if (w->error[0])
|
|
||||||
{
|
{
|
||||||
DPrint(w->error);
|
std::string_view srcpakv = srcpak.view();
|
||||||
}
|
char* argv[1];
|
||||||
if (w->engine != nullptr) {
|
argv[0] = const_cast<char*>("lpxserver");
|
||||||
DPrint("Luprex initialize success");
|
w->play_initialize(w.Get(), 1, argv, srcpakv.size(), srcpakv.data(), "");
|
||||||
Playing = true;
|
if (w->error[0])
|
||||||
|
{
|
||||||
|
DPrint(w->error);
|
||||||
|
}
|
||||||
|
if (w->engine != nullptr) {
|
||||||
|
DPrint("Luprex initialize success");
|
||||||
|
Playing = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -237,7 +252,8 @@ void AIntegrationGameModeBase::BeginPlay()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Initialize the tangible manager.
|
// Initialize the tangible manager.
|
||||||
TangibleManager.Init(GetWorld(), ClassTangibleActor);
|
TangibleManager = NewObject<UlxTangibleManager>();
|
||||||
|
TangibleManager->Init(GetWorld(), ClassTangibleActor);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AIntegrationGameModeBase::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
void AIntegrationGameModeBase::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
||||||
@@ -245,4 +261,3 @@ void AIntegrationGameModeBase::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
|||||||
ResetToInitialState();
|
ResetToInitialState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -43,6 +43,9 @@ public:
|
|||||||
UPROPERTY(EditDefaultsOnly, Category = "Luprex")
|
UPROPERTY(EditDefaultsOnly, Category = "Luprex")
|
||||||
TSubclassOf<AActor> ClassTangibleActor;
|
TSubclassOf<AActor> ClassTangibleActor;
|
||||||
|
|
||||||
|
// Execute a debugging command, typed on the GUI.
|
||||||
|
void ExecuteDebuggingCommand(const FString &fs);
|
||||||
|
|
||||||
// Transfer console output from the Luprex engine to unreal.
|
// Transfer console output from the Luprex engine to unreal.
|
||||||
void UpdateConsoleOutput();
|
void UpdateConsoleOutput();
|
||||||
|
|
||||||
@@ -57,7 +60,7 @@ public:
|
|||||||
virtual uint32 Run() override;
|
virtual uint32 Run() override;
|
||||||
|
|
||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
FTangibleManager TangibleManager;
|
UlxTangibleManager *TangibleManager;
|
||||||
|
|
||||||
// This stores the entire text currently visible in the console.
|
// This stores the entire text currently visible in the console.
|
||||||
FlxConsoleOutput ConsoleOutput;
|
FlxConsoleOutput ConsoleOutput;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
#include "LockedWrapper.h"
|
#include "LockedWrapper.h"
|
||||||
#include "DebugPrint.h"
|
#include "DebugPrint.h"
|
||||||
#include "lpx-drvutil.hpp"
|
#include "lpx-drvutil.hpp"
|
||||||
|
#include "lpx-paths.hpp"
|
||||||
|
|
||||||
using namespace CommonTypes;
|
using namespace CommonTypes;
|
||||||
|
|
||||||
@@ -10,7 +11,9 @@ void FlxLockedWrapper::InitWrapper() {
|
|||||||
// Already initialized.
|
// Already initialized.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
void* DLL = FPlatformProcess::GetDllHandle(TEXT("c:\\Luprex\\build\\visual\\luprexlib.dll"));
|
FString dll((const UTF8CHAR*)LUPREX_DLL_PATH);
|
||||||
|
DebugPrint::DPrint(dll);
|
||||||
|
void* DLL = FPlatformProcess::GetDllHandle(*dll);
|
||||||
if (DLL != nullptr) {
|
if (DLL != nullptr) {
|
||||||
using InitFn = void (*)(EngineWrapper*);
|
using InitFn = void (*)(EngineWrapper*);
|
||||||
InitFn init = (InitFn)FPlatformProcess::GetDllExport(DLL, TEXT("init_engine_wrapper"));
|
InitFn init = (InitFn)FPlatformProcess::GetDllExport(DLL, TEXT("init_engine_wrapper"));
|
||||||
@@ -43,7 +46,7 @@ int64 FlxLockedWrapper::GetActor() {
|
|||||||
IdView FlxLockedWrapper::GetNear(int64 id, double rx, double ry, double rz) {
|
IdView FlxLockedWrapper::GetNear(int64 id, double rx, double ry, double rz) {
|
||||||
uint32 size;
|
uint32 size;
|
||||||
int64* data;
|
int64* data;
|
||||||
Lockable.Wrapper.get_tangibles_near(Get(), id, rx, ry, rz, &size, &data);
|
Lockable.Wrapper.get_tangibles_near(Get(), id, rx, ry, rz, &size, (int64_t**)&data);
|
||||||
return IdView(data, size);
|
return IdView(data, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,7 +67,7 @@ StringViewVec FlxLockedWrapper::GetAnimationQueues(IdView ids) {
|
|||||||
const char** StrBuf = Lockable.AQStrBuffer.GetData();
|
const char** StrBuf = Lockable.AQStrBuffer.GetData();
|
||||||
|
|
||||||
// Get the animation queues into the static buffers.
|
// Get the animation queues into the static buffers.
|
||||||
Lockable.Wrapper.get_animation_queues(Get(), num, ids.GetData(), LenBuf, StrBuf);
|
Lockable.Wrapper.get_animation_queues(Get(), num, (const int64_t *)ids.GetData(), LenBuf, StrBuf);
|
||||||
|
|
||||||
// Transfer data from static buffers into an array of string_view
|
// Transfer data from static buffers into an array of string_view
|
||||||
StringViewVec result;
|
StringViewVec result;
|
||||||
|
|||||||
@@ -23,9 +23,13 @@ THIRD_PARTY_INCLUDES_START
|
|||||||
THIRD_PARTY_INCLUDES_END
|
THIRD_PARTY_INCLUDES_END
|
||||||
#undef UI
|
#undef UI
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
#else
|
||||||
#define WIN32_LEAN_AND_MEAN
|
#define WIN32_LEAN_AND_MEAN
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <wincrypt.h>
|
#include <wincrypt.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
@@ -241,6 +245,26 @@ public:
|
|||||||
//
|
//
|
||||||
/////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
inline static void strerror_helper(int status, int errnum, char errbuf[256]) {
|
||||||
|
if (status != 0) {
|
||||||
|
snprintf(errbuf, 256, "unknown errno %d", errnum);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline static void strerror_helper(const char *result, int errnum, char errbuf[256]) {
|
||||||
|
if (result != errbuf) {
|
||||||
|
snprintf(errbuf, 256, "%s", result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string strerror_str(int errnum) {
|
||||||
|
char buf[256];
|
||||||
|
auto rval = strerror_r(errnum, buf, 256);
|
||||||
|
strerror_helper(rval, errnum, buf);
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
#else
|
||||||
static std::string strerror_str(int errnum) {
|
static std::string strerror_str(int errnum) {
|
||||||
char buf[256];
|
char buf[256];
|
||||||
int status = strerror_s(buf, 256, errnum);
|
int status = strerror_s(buf, 256, errnum);
|
||||||
@@ -250,6 +274,8 @@ static std::string strerror_str(int errnum) {
|
|||||||
}
|
}
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
static FSocket* OpenConnection(ISocketSubsystem *subsys, const std::string& host, const std::string& port, std::string& err)
|
static FSocket* OpenConnection(ISocketSubsystem *subsys, const std::string& host, const std::string& port, std::string& err)
|
||||||
{
|
{
|
||||||
@@ -401,6 +427,12 @@ static SSL_CTX* SSLNewContext(int verify, const SSL_METHOD *method, BIO *tracebi
|
|||||||
return ctx;
|
return ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
static std::string SSLLoadCertificateAuthorities(SSL_CTX* ctx) {
|
||||||
|
check(SSL_CTX_set_default_verify_paths(ctx) == 1);
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
#else
|
||||||
static std::string SSLLoadCertificateAuthorities(SSL_CTX* ctx) {
|
static std::string SSLLoadCertificateAuthorities(SSL_CTX* ctx) {
|
||||||
HCERTSTORE hStore = CertOpenSystemStoreW(0, L"ROOT");
|
HCERTSTORE hStore = CertOpenSystemStoreW(0, L"ROOT");
|
||||||
|
|
||||||
@@ -427,6 +459,7 @@ static std::string SSLLoadCertificateAuthorities(SSL_CTX* ctx) {
|
|||||||
CertCloseStore(hStore, 0);
|
CertCloseStore(hStore, 0);
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static std::string SSLUseCertificateString(SSL_CTX* ctx, const char* str) {
|
static std::string SSLUseCertificateString(SSL_CTX* ctx, const char* str) {
|
||||||
SSLClearErrors();
|
SSLClearErrors();
|
||||||
@@ -747,7 +780,7 @@ void FLpxChannel::Advance()
|
|||||||
AdvanceReadWrite();
|
AdvanceReadWrite();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
checkf(false, L"EChanState is invalid");
|
checkf(false, TEXT("EChanState is invalid"));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
34
Source/Integration/SampleActorComponent.cpp
Normal file
34
Source/Integration/SampleActorComponent.cpp
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
// Fill out your copyright notice in the Description page of Project Settings.
|
||||||
|
|
||||||
|
|
||||||
|
#include "SampleActorComponent.h"
|
||||||
|
|
||||||
|
// Sets default values for this component's properties
|
||||||
|
USampleActorComponent::USampleActorComponent()
|
||||||
|
{
|
||||||
|
// Set this component to be initialized when the game starts, and to be ticked every frame. You can turn these features
|
||||||
|
// off to improve performance if you don't need them.
|
||||||
|
PrimaryComponentTick.bCanEverTick = true;
|
||||||
|
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Called when the game starts
|
||||||
|
void USampleActorComponent::BeginPlay()
|
||||||
|
{
|
||||||
|
Super::BeginPlay();
|
||||||
|
|
||||||
|
// ...
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Called every frame
|
||||||
|
void USampleActorComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
|
||||||
|
{
|
||||||
|
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
|
||||||
|
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
28
Source/Integration/SampleActorComponent.h
Normal file
28
Source/Integration/SampleActorComponent.h
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
// Fill out your copyright notice in the Description page of Project Settings.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "CoreMinimal.h"
|
||||||
|
#include "Components/ActorComponent.h"
|
||||||
|
#include "SampleActorComponent.generated.h"
|
||||||
|
|
||||||
|
|
||||||
|
UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
|
||||||
|
class INTEGRATION_API USampleActorComponent : public UActorComponent
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Sets default values for this component's properties
|
||||||
|
USampleActorComponent();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// Called when the game starts
|
||||||
|
virtual void BeginPlay() override;
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Called every frame
|
||||||
|
virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
@@ -1,26 +1,2 @@
|
|||||||
#include "StringDecoder.h"
|
#include "StringDecoder.h"
|
||||||
|
|
||||||
FlxStringDecoder::FlxStringDecoder(std::string_view s) {
|
|
||||||
Text = s.data();
|
|
||||||
Size = s.size();
|
|
||||||
ErrBeyondEOF = false;
|
|
||||||
ErrStringTooLong = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string_view FlxStringDecoder::read_string_view() {
|
|
||||||
size_t length = read_length();
|
|
||||||
if (length > Size) {
|
|
||||||
ErrBeyondEOF = true;
|
|
||||||
return std::string_view();
|
|
||||||
}
|
|
||||||
std::string_view result(Text, length);
|
|
||||||
Text += length;
|
|
||||||
Size -= length;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FlxStringDecoder::set_at_eof() {
|
|
||||||
Text += Size;
|
|
||||||
Size = 0;
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,116 +1,46 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "CoreMinimal.h"
|
#include "CoreMinimal.h"
|
||||||
#include "lpx-basewriter.hpp"
|
#include "lpx-basebuffer.hpp"
|
||||||
|
|
||||||
/////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// FlxStringDecoder
|
|
||||||
//
|
|
||||||
// This class is used to decipher 8-bit strings that
|
|
||||||
// contain packed ints, strings, and other data.
|
|
||||||
// The typical example of usage is to decipher the
|
|
||||||
// serialized animation queues fed to us by Luprex.
|
|
||||||
//
|
|
||||||
// The FlxStringDecoder doesn't make a copy of the string
|
|
||||||
// you pass in, instead, it stores a pointer to it.
|
|
||||||
// So be sure not to free the string until you're
|
|
||||||
// done analyzing it.
|
|
||||||
//
|
|
||||||
// You'll note that some of the function names are
|
|
||||||
// lowercase, with underscores. That's because they're
|
|
||||||
// inherited from Luprex classes, and luprex classes
|
|
||||||
// use that naming convention. There's not any easy
|
|
||||||
// workaround for that.
|
|
||||||
//
|
|
||||||
/////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
|
|
||||||
class FlxStringDecoder : public BaseReader<FlxStringDecoder> {
|
using FlxSimpleDynamic = SimpleDynamic<std::string>;
|
||||||
|
|
||||||
|
class FlxStreamBufferCore {
|
||||||
private:
|
private:
|
||||||
const char* Text;
|
bool err_eof_on_read_;
|
||||||
size_t Size;
|
bool err_string_too_long_;
|
||||||
|
bool err_integer_truncated_;
|
||||||
|
protected:
|
||||||
|
|
||||||
public:
|
void *basebuffer_malloc(size_t size) { return malloc(size); }
|
||||||
// You can check and clear these error flags at will.
|
void basebuffer_free(void *p) { free(p); }
|
||||||
//
|
void clear_error_flags() { err_eof_on_read_ = err_string_too_long_ = err_integer_truncated_ = false; }
|
||||||
bool ErrBeyondEOF;
|
void raise_eof_on_read() { err_eof_on_read_ = true; }
|
||||||
bool ErrStringTooLong;
|
void raise_string_too_long() { err_string_too_long_ = true; }
|
||||||
|
void raise_integer_truncated() { err_integer_truncated_ = true; }
|
||||||
|
|
||||||
public:
|
bool get_err_eof_on_read() const { return err_eof_on_read_; }
|
||||||
// This function is required by BaseReader.
|
bool get_err_string_too_long() const { return err_string_too_long_; }
|
||||||
// It's not meant for end users.
|
bool get_err_integer_truncated() const { return err_integer_truncated_; }
|
||||||
//
|
bool any_error() const { return err_eof_on_read_ || err_string_too_long_ || err_integer_truncated_; }
|
||||||
void read_bytes_into(char* buffer, size_t size) {
|
|
||||||
if (size > Size) {
|
|
||||||
memset(buffer, 0, size);
|
|
||||||
ErrBeyondEOF = true;
|
|
||||||
set_at_eof();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
memcpy(buffer, Text, size);
|
|
||||||
Text += size;
|
|
||||||
Size -= size;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This function is required by BaseReader.
|
|
||||||
// It's not meant for end users.
|
|
||||||
//
|
|
||||||
void raise_string_too_long() {
|
|
||||||
ErrStringTooLong = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
// The type returned by read_string.
|
|
||||||
//
|
|
||||||
using read_string_type = std::string;
|
|
||||||
|
|
||||||
// Initialize the string decoder with a text to analyze.
|
|
||||||
//
|
|
||||||
FlxStringDecoder(std::string_view s);
|
|
||||||
|
|
||||||
// Get the size of the remaining text.
|
|
||||||
//
|
|
||||||
size_t get_size() { return Size; }
|
|
||||||
|
|
||||||
// Return true if the remaining text is empty.
|
|
||||||
//
|
|
||||||
bool at_eof() { return Size == 0; }
|
|
||||||
|
|
||||||
// Move the cursor to EOF.
|
|
||||||
//
|
|
||||||
void set_at_eof();
|
|
||||||
|
|
||||||
// Read a string as a string_view
|
|
||||||
//
|
|
||||||
// This reads a string from the source, returning
|
|
||||||
// it as a string_view that points into the buffer.
|
|
||||||
// Naturally, if you release the buffer, the
|
|
||||||
// string_view is invalidated.
|
|
||||||
//
|
|
||||||
// This is considerably faster than read_string.
|
|
||||||
//
|
|
||||||
std::string_view read_string_view();
|
|
||||||
|
|
||||||
// Inherited Methods:
|
|
||||||
//
|
|
||||||
// The following methods are inherited from BaseReader:
|
|
||||||
//
|
|
||||||
// uint8_t read_uint8();
|
|
||||||
// uint16_t read_uint16();
|
|
||||||
// uint32_t read_uint32();
|
|
||||||
// uint64_t read_uint64();
|
|
||||||
// int8_t read_int8();
|
|
||||||
// int16_t read_int16();
|
|
||||||
// int32_t read_int32();
|
|
||||||
// int64_t read_int64();
|
|
||||||
// bool read_bool();
|
|
||||||
// char read_char();
|
|
||||||
// float read_float();
|
|
||||||
// double read_double();
|
|
||||||
// size_t read_length();
|
|
||||||
// std::string read_string_limit(uint64_t size);
|
|
||||||
// std::string read_string();
|
|
||||||
//
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class FlxStreamBuffer : public BaseBuffer<FlxStreamBufferCore, std::string> {
|
||||||
|
public:
|
||||||
|
using BaseBuffer::BaseBuffer;
|
||||||
|
|
||||||
|
void write_fvector(const FVector &xyz) {
|
||||||
|
write_double(xyz.X);
|
||||||
|
write_double(xyz.Y);
|
||||||
|
write_double(xyz.Z);
|
||||||
|
}
|
||||||
|
|
||||||
|
FVector read_fvector() {
|
||||||
|
double x = read_double();
|
||||||
|
double y = read_double();
|
||||||
|
double z = read_double();
|
||||||
|
return FVector(x, y, z);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,4 +2,132 @@
|
|||||||
|
|
||||||
|
|
||||||
#include "Tangible.h"
|
#include "Tangible.h"
|
||||||
|
#include "TangibleManager.h"
|
||||||
|
|
||||||
|
UlxTangible::UlxTangible()
|
||||||
|
{
|
||||||
|
Manager = nullptr;
|
||||||
|
TangibleId = -1;
|
||||||
|
CurrentActor = nullptr;
|
||||||
|
ActorBlueprint = nullptr;
|
||||||
|
NearAccordingToLuprex = false;
|
||||||
|
NearAccordingToUnreal = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UlxTangible::Init(UlxTangibleManager* tm, int64 id)
|
||||||
|
{
|
||||||
|
Manager = tm;
|
||||||
|
TangibleId = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UlxTangible::BlueprintIsTangible(TSubclassOf<AActor> bp) {
|
||||||
|
if (bp == nullptr) return true;
|
||||||
|
return bp->ImplementsInterface(UlxTangibleInterface::StaticClass());
|
||||||
|
}
|
||||||
|
|
||||||
|
void UlxTangible::SetActorBlueprintClass(TSubclassOf<AActor> bp) {
|
||||||
|
// If we're already of the right class, do nothing.
|
||||||
|
if (ActorBlueprint == bp) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sanity check the blueprint. Nullptr is allowed.
|
||||||
|
check(BlueprintIsTangible(bp));
|
||||||
|
|
||||||
|
// If there's already an actor, delete it.
|
||||||
|
if (CurrentActor != nullptr) {
|
||||||
|
// Remove the tangible component. This is probably
|
||||||
|
// unnecessary, but it makes it more likely that we'll
|
||||||
|
// catch bugs early.
|
||||||
|
UlxTangibleComponent* comp = CurrentActor->GetComponentByClass<UlxTangibleComponent>();
|
||||||
|
if (comp != nullptr) {
|
||||||
|
comp->DestroyComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now destroy the actor itself. According to various
|
||||||
|
// documents I've read online, it may be necessary to take
|
||||||
|
// further steps to delete the object. Not clear.
|
||||||
|
CurrentActor->Destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the blueprint reference.
|
||||||
|
ActorBlueprint = bp;
|
||||||
|
|
||||||
|
// Now create a new actor, unless the BP is nullptr.
|
||||||
|
if (ActorBlueprint != nullptr) {
|
||||||
|
// Create the actor.
|
||||||
|
FActorSpawnParameters params;
|
||||||
|
FVector location(0, 0, 0);
|
||||||
|
FRotator rotation(0, 0, 0);
|
||||||
|
UWorld* w = Manager->GetWorld();
|
||||||
|
AActor* a = w->SpawnActor(ActorBlueprint, &location, &rotation, params);
|
||||||
|
|
||||||
|
// Insert a TangibleComponent into the actor.
|
||||||
|
UActorComponent* ac = a->AddComponentByClass(UlxTangibleComponent::StaticClass(), false, FTransform::Identity, false);
|
||||||
|
UlxTangibleComponent* tc = Cast<UlxTangibleComponent>(ac);
|
||||||
|
check(tc != nullptr);
|
||||||
|
|
||||||
|
// Make the tangible point to the actor and vice versa.
|
||||||
|
tc->Tangible = this;
|
||||||
|
CurrentActor = a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UlxTangible::UpdateAnimationQueue(std::string_view aq) {
|
||||||
|
AnimTracker.Update(aq);
|
||||||
|
int limit = 3;
|
||||||
|
while (AnimTracker.IsChanged()) {
|
||||||
|
if (limit == 0) break;
|
||||||
|
limit -= 1;
|
||||||
|
AnimTracker.ClearChanged();
|
||||||
|
IlxTangibleInterface::Execute_AnimationStateChanged(GetActor());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FVector UlxTangible::GetLocation() const {
|
||||||
|
if (CurrentActor == nullptr) {
|
||||||
|
return FVector(0,0,0);
|
||||||
|
} else {
|
||||||
|
return CurrentActor->GetActorLocation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UlxTangible::Destroy() {
|
||||||
|
SetActorBlueprintClass(nullptr);
|
||||||
|
Manager = nullptr;
|
||||||
|
TangibleId = -1;
|
||||||
|
CurrentActor = nullptr;
|
||||||
|
ActorBlueprint = nullptr;
|
||||||
|
AnimTracker.Clear();
|
||||||
|
NearAccordingToLuprex = false;
|
||||||
|
NearAccordingToUnreal = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static UlxTangible *GetActorTangible(AActor *actor) {
|
||||||
|
UlxTangibleComponent* comp = actor->GetComponentByClass<UlxTangibleComponent>();
|
||||||
|
check(comp != nullptr);
|
||||||
|
UlxTangible *result = comp->Tangible.Get();
|
||||||
|
check(result != nullptr);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UlxTangible::GetCurrentAnimation(AActor *target, FlxAnimationStep &step) {
|
||||||
|
step = GetActorTangible(target)->AnimTracker.GetCurrentAnimation();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UlxTangible::FinishedAnimation(AActor *target, const FlxAnimationStep &step, bool autoxyz, bool autofacing, bool autoplane) {
|
||||||
|
UlxTangible *tan = GetActorTangible(target);
|
||||||
|
if (autoxyz) step.AutoUpdateXYZ(target);
|
||||||
|
if (autofacing) step.AutoUpdateFacing(target);
|
||||||
|
if (autoplane) step.AutoUpdatePlane(&(tan->Plane));
|
||||||
|
tan->AnimTracker.FinishedAnimation(step.Hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
FString UlxTangible::GetTangiblePlane(AActor* target) {
|
||||||
|
return GetActorTangible(target)->Plane.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UlxTangible::SetTangiblePlane(AActor* target, const FString& plane) {
|
||||||
|
GetActorTangible(target)->Plane = FName(plane);
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,27 +3,203 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "CoreMinimal.h"
|
#include "CoreMinimal.h"
|
||||||
#include "UObject/NoExportTypes.h"
|
#include "Components/ActorComponent.h"
|
||||||
#include "AnimQueue.h"
|
#include "AnimQueue.h"
|
||||||
#include "TangibleInterface.h"
|
|
||||||
#include "Tangible.generated.h"
|
#include "Tangible.generated.h"
|
||||||
|
|
||||||
|
|
||||||
|
class UlxTangibleManager;
|
||||||
|
|
||||||
|
// This class does not need to be modified.
|
||||||
|
UINTERFACE(Blueprintable)
|
||||||
|
class UlxTangibleInterface : public UInterface
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
*
|
||||||
|
* IlxTangibleInterface
|
||||||
|
*
|
||||||
|
* This class implements the interface that an Actor must implement
|
||||||
|
* in order for that Actor to be usable as a Tangible.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
class INTEGRATION_API IlxTangibleInterface
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
// Add interface functions to this class. This is the class that will be inherited to implement this interface.
|
||||||
|
public:
|
||||||
|
UFUNCTION(BlueprintImplementableEvent, Category = "Tangible Functionality")
|
||||||
|
bool AnimationStateChanged();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* UlxTangible
|
||||||
|
*
|
||||||
|
* The Tangible stores all the data we need for a tangible,
|
||||||
|
* such as the animation queue and so forth.
|
||||||
|
*
|
||||||
|
* From time to time, a tangible can change its blueprint class.
|
||||||
|
* To do that, we have to delete and recreate the actor. This
|
||||||
|
* is all set up so that it is possible to do that.
|
||||||
|
*
|
||||||
|
* The tangible has a place to store an Actor pointer. This
|
||||||
|
* actor pointer is allowed to be nullptr, especially in the
|
||||||
|
* case that the blueprint hasn't been set yet.
|
||||||
|
*
|
||||||
|
* This also serves as a repository for blueprint functions
|
||||||
|
* that operate on tangible actors.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
UCLASS()
|
UCLASS()
|
||||||
class INTEGRATION_API UlxTangible : public UObject
|
class INTEGRATION_API UlxTangible : public UObject
|
||||||
{
|
{
|
||||||
GENERATED_BODY()
|
GENERATED_BODY()
|
||||||
|
|
||||||
public:
|
public:
|
||||||
UPROPERTY()
|
UlxTangible();
|
||||||
AActor* Actor;
|
|
||||||
|
|
||||||
|
// My Tangible Manager.
|
||||||
|
UPROPERTY()
|
||||||
|
TObjectPtr<UlxTangibleManager> Manager;
|
||||||
|
|
||||||
|
// The tangible ID.
|
||||||
|
UPROPERTY()
|
||||||
|
uint64 TangibleId;
|
||||||
|
|
||||||
|
UPROPERTY()
|
||||||
|
TWeakObjectPtr<AActor> CurrentActor;
|
||||||
|
|
||||||
|
// The blueprint class of the actor.
|
||||||
|
UPROPERTY()
|
||||||
|
TSubclassOf<AActor> ActorBlueprint;
|
||||||
|
|
||||||
|
// Animation tracker
|
||||||
FlxAnimTracker AnimTracker;
|
FlxAnimTracker AnimTracker;
|
||||||
|
|
||||||
void Init(AActor* a) {
|
// Current Plane.
|
||||||
Actor = a;
|
FName Plane;
|
||||||
}
|
|
||||||
|
// True if luprex thinks this object is Near the player.
|
||||||
|
bool NearAccordingToLuprex;
|
||||||
|
|
||||||
|
// True if unreal thinks this object is Near the player.
|
||||||
|
bool NearAccordingToUnreal;
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Initialize a new tangible.
|
||||||
|
//
|
||||||
|
// This links the tangible to its TangibleManager and
|
||||||
|
// sets the tangible's ID.
|
||||||
|
//
|
||||||
|
void Init(UlxTangibleManager *tm, int64 id);
|
||||||
|
|
||||||
|
// Destroy a tangible.
|
||||||
|
//
|
||||||
|
// Delete the actor associated with the tangible, if any,
|
||||||
|
// and clean up everything else.
|
||||||
|
//
|
||||||
|
void Destroy();
|
||||||
|
|
||||||
|
// Get the actor associated with this tangible.
|
||||||
|
//
|
||||||
|
// Note that this may return nullptr: it is valid for a
|
||||||
|
// tangible to have no actor associated with it. This
|
||||||
|
// is most commonly the case if the blueprint class of
|
||||||
|
// the tangible is set to nullptr.
|
||||||
|
//
|
||||||
|
// Also bear in mind that if a tangible changes its blueprint
|
||||||
|
// class, then the actor will have to be deleted and
|
||||||
|
// recreated. In that case, GetActor will return a different
|
||||||
|
// pointer than before the blueprint change.
|
||||||
|
//
|
||||||
|
AActor* GetActor() const { return CurrentActor.Get(); }
|
||||||
|
|
||||||
|
// Get the location of the tangible.
|
||||||
|
//
|
||||||
|
// Note that if the actor is nullptr, the location is always
|
||||||
|
// at the origin.
|
||||||
|
//
|
||||||
|
FVector GetLocation() const;
|
||||||
|
|
||||||
|
// Check a blueprint class to see if it is valid as a Tangible.
|
||||||
|
//
|
||||||
|
// In order for a blueprint class to be used as a tangible,
|
||||||
|
// it must implement the interface IlxTangibleInterface.
|
||||||
|
// This function also returns true for nullptr.
|
||||||
|
//
|
||||||
|
static bool BlueprintIsTangible(TSubclassOf<AActor> bp);
|
||||||
|
|
||||||
|
// Change the blueprint class of the tangible.
|
||||||
|
//
|
||||||
|
// This requires the deletion and recreation of the Actor.
|
||||||
|
// The blueprint class must satisfy 'BlueprintIsTangible' above.
|
||||||
|
//
|
||||||
|
// It is legal to pass in nullptr for the blueprint class.
|
||||||
|
// Whenever the blueprint class is nullptr, the Actor is
|
||||||
|
// also nullptr. Ie, a tangible will have no Actor associated
|
||||||
|
// with it if its blueprint class is nullptr.
|
||||||
|
//
|
||||||
|
void SetActorBlueprintClass(TSubclassOf<AActor> bp);
|
||||||
|
|
||||||
|
// Update the animation queue from Luprex.
|
||||||
|
//
|
||||||
|
// This reads the animation queue, and then based on
|
||||||
|
// what is new in the animation queue, it calls into
|
||||||
|
// the Actor's TangibleInterface, calling methods such
|
||||||
|
// as 'StartAnimation' and 'AbortAnimation' as necessary.
|
||||||
|
//
|
||||||
|
// If the animation queue specifies a blueprint change,
|
||||||
|
// this function will eventually implement that by calling
|
||||||
|
// SetActorBlueprintClass above. This is not implemented
|
||||||
|
// yet.
|
||||||
|
//
|
||||||
|
void UpdateAnimationQueue(std::string_view aq);
|
||||||
|
|
||||||
|
public:
|
||||||
|
UFUNCTION(BlueprintCallable, Meta = (DefaultToSelf = "target"), Category = Luprex)
|
||||||
|
static void GetCurrentAnimation(AActor *target, FlxAnimationStep &step);
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable, Meta = (DefaultToSelf = "target"), Category = Luprex)
|
||||||
|
static void FinishedAnimation(AActor *target, const FlxAnimationStep &step,
|
||||||
|
bool AutoUpdateXYZ = true, bool AutoUpdateFacing = true, bool AutoUpdatePlane = true);
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable, Meta = (DefaultToSelf = "target"), Category = Luprex)
|
||||||
|
static FString GetTangiblePlane(AActor* target);
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable, Meta = (DefaultToSelf = "target"), Category = Luprex)
|
||||||
|
static void SetTangiblePlane(AActor* target, const FString& plane);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* UlxTangibleComponent
|
||||||
|
*
|
||||||
|
* The TangibleComponent holds a pointer to the Tangible.
|
||||||
|
* This is the only purpose it serves: to make it possible to
|
||||||
|
* have the Actor point to its corresponding Tangible.
|
||||||
|
*
|
||||||
|
* The TangibleComponent is procedurally inserted into the Actor.
|
||||||
|
* The TangibleComponent is not visible to blueprints.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
|
||||||
|
class INTEGRATION_API UlxTangibleComponent : public UActorComponent
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
public:
|
||||||
|
UlxTangibleComponent() : Tangible(nullptr) {}
|
||||||
|
|
||||||
|
// The actor that we're a part of.
|
||||||
|
UPROPERTY()
|
||||||
|
TWeakObjectPtr<UlxTangible> Tangible;
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
// Fill out your copyright notice in the Description page of Project Settings.
|
|
||||||
|
|
||||||
|
|
||||||
#include "TangibleInterface.h"
|
|
||||||
|
|
||||||
// Add default functionality here for any IlxTangibleInterface functions that are not pure virtual.
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
// Fill out your copyright notice in the Description page of Project Settings.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "CoreMinimal.h"
|
|
||||||
#include "UObject/Interface.h"
|
|
||||||
#include "AnimQueue.h"
|
|
||||||
#include "TangibleInterface.generated.h"
|
|
||||||
|
|
||||||
// This class does not need to be modified.
|
|
||||||
UINTERFACE(Blueprintable)
|
|
||||||
class UlxTangibleInterface : public UInterface
|
|
||||||
{
|
|
||||||
GENERATED_BODY()
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
class INTEGRATION_API IlxTangibleInterface
|
|
||||||
{
|
|
||||||
GENERATED_BODY()
|
|
||||||
|
|
||||||
// Add interface functions to this class. This is the class that will be inherited to implement this interface.
|
|
||||||
public:
|
|
||||||
UFUNCTION(BlueprintImplementableEvent, Category = "Luprex")
|
|
||||||
bool StartAnimation(ElxAnimationMode mode, const FlxAnimationStep& step);
|
|
||||||
|
|
||||||
UFUNCTION(BlueprintImplementableEvent, Category = "Luprex")
|
|
||||||
bool AbortAnimation(int64 hash);
|
|
||||||
};
|
|
||||||
@@ -2,27 +2,25 @@
|
|||||||
|
|
||||||
|
|
||||||
#include "TangibleManager.h"
|
#include "TangibleManager.h"
|
||||||
#include "TangibleInterface.h"
|
#include "Tangible.h"
|
||||||
#include "DebugPrint.h"
|
#include "DebugPrint.h"
|
||||||
|
|
||||||
using namespace DebugPrint;
|
using namespace DebugPrint;
|
||||||
|
using TanArray = UlxTangibleManager::TanArray;
|
||||||
|
using IdArray = UlxTangibleManager::IdArray;
|
||||||
|
|
||||||
FTangibleManager::FTangibleManager() {
|
UlxTangibleManager::UlxTangibleManager() {
|
||||||
World = nullptr;
|
World = nullptr;
|
||||||
ClassTangibleActor = nullptr;
|
ClassTangibleActor = nullptr;
|
||||||
Actor = 0;
|
|
||||||
Near = IdView();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FTangibleManager::Init(UWorld *world, UClass* tanact) {
|
void UlxTangibleManager::Init(UWorld* world, UClass* tanact) {
|
||||||
World = world;
|
World = world;
|
||||||
ClassTangibleActor = tanact;
|
ClassTangibleActor = tanact;
|
||||||
Actor = 0;
|
|
||||||
Near = IdView();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
UlxTangible *FTangibleManager::GetTangible(int64 id) {
|
UlxTangible* UlxTangibleManager::GetTangible(int64 id) const {
|
||||||
UlxTangible **p = IdToTangible.Find(id);
|
UlxTangible*const* p = IdToTangible.Find(id);
|
||||||
if (p == nullptr) {
|
if (p == nullptr) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
} else {
|
} else {
|
||||||
@@ -30,31 +28,90 @@ UlxTangible *FTangibleManager::GetTangible(int64 id) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
UlxTangible *FTangibleManager::MakeTangible(int64 id) {
|
#pragma optimize("", off)
|
||||||
UlxTangible *& p = IdToTangible.FindOrAdd(id);
|
UlxTangible* UlxTangibleManager::MakeTangible(int64 id) {
|
||||||
if (p == nullptr) {
|
UlxTangible*& t = IdToTangible.FindOrAdd(id);
|
||||||
FVector location(0,0,0);
|
if (t == nullptr) {
|
||||||
FRotator rotation(0, 0, 0);
|
t = NewObject<UlxTangible>();
|
||||||
FActorSpawnParameters params;
|
t->Init(this, id);
|
||||||
AActor* a = World->SpawnActor(ClassTangibleActor, &location, &rotation, params);
|
|
||||||
check(a != nullptr);
|
// TODO: fix this. The blueprint needs to be assigned
|
||||||
check(a->GetClass()->ImplementsInterface(UlxTangibleInterface::StaticClass()));
|
// during animation queue parsing, based on the animation
|
||||||
p = NewObject<UlxTangible>();
|
// queue keyword 'bp'.
|
||||||
p->Init(a);
|
t->SetActorBlueprintClass(ClassTangibleActor);
|
||||||
}
|
}
|
||||||
return p;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FTangibleManager::DeleteTangible(int64 id) {
|
void UlxTangibleManager::DeleteTangible(int64 id) {
|
||||||
// IMPLEMENT ME
|
// IMPLEMENT ME
|
||||||
}
|
}
|
||||||
|
|
||||||
FTangibleManager::IdArray FTangibleManager::GetLive() {
|
TanArray UlxTangibleManager::GetAllTangibles() const {
|
||||||
IdArray result;
|
TanArray result;
|
||||||
result.SetNum(IdToTangible.Num());
|
result.SetNum(IdToTangible.Num());
|
||||||
int next = 0;
|
int next = 0;
|
||||||
for (auto &pair : IdToTangible) {
|
for (auto& pair : IdToTangible) {
|
||||||
result[next++] = pair.Key;
|
result[next++] = pair.Value;
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#pragma optimize("", off)
|
||||||
|
void UlxTangibleManager::UpdateNearAccordingToLuprex(IdView near) {
|
||||||
|
// Clear all the 'NearAccordingToLuprex' flags.
|
||||||
|
for (const auto& pair : IdToTangible) {
|
||||||
|
pair.Value->NearAccordingToLuprex = false;
|
||||||
|
}
|
||||||
|
// For every ID on the list, create it if it doesn't exist,
|
||||||
|
// mark it, and return it.
|
||||||
|
for (int64 id : near) {
|
||||||
|
UlxTangible* tan = MakeTangible(id);
|
||||||
|
tan->NearAccordingToLuprex = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma optimize("", off)
|
||||||
|
void UlxTangibleManager::RecalcNearAccordingToUnreal(int64 player, double radius) {
|
||||||
|
UlxTangible *p = GetTangible(player);
|
||||||
|
check (p != nullptr);
|
||||||
|
FVector playerpos = p->CurrentActor->GetActorLocation();
|
||||||
|
FName playerplane = p->Plane;
|
||||||
|
double radiussq = radius * radius;
|
||||||
|
for (const auto& pair : IdToTangible) {
|
||||||
|
UlxTangible *tan = pair.Value;
|
||||||
|
if (tan->Plane != playerplane) {
|
||||||
|
tan->NearAccordingToUnreal = false;
|
||||||
|
} else {
|
||||||
|
FVector pos = tan->GetLocation();
|
||||||
|
double distsq = FVector::DistSquared(pos, playerpos);
|
||||||
|
tan->NearAccordingToUnreal = (distsq <= radiussq);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UlxTangibleManager::DeleteFarawayTangibles() {
|
||||||
|
// Make a list of tangibles that need to be deleted.
|
||||||
|
TanArray faraway;
|
||||||
|
for (const auto& pair : IdToTangible) {
|
||||||
|
UlxTangible *tan = pair.Value;
|
||||||
|
if (!(tan->NearAccordingToLuprex || tan->NearAccordingToUnreal)) {
|
||||||
|
faraway.Add(tan);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (UlxTangible *tan : faraway) {
|
||||||
|
IdToTangible.Remove(tan->TangibleId);
|
||||||
|
tan->Destroy(); // Remove the actor from the scene.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IdArray UlxTangibleManager::GetIds(const TanArray &arr) {
|
||||||
|
IdArray result;
|
||||||
|
result.SetNum(arr.Num());
|
||||||
|
for (int i = 0; i < arr.Num(); i++) {
|
||||||
|
result[i] = arr[i]->TangibleId;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,62 +8,77 @@
|
|||||||
#include "Tangible.h"
|
#include "Tangible.h"
|
||||||
#include "TangibleManager.generated.h"
|
#include "TangibleManager.generated.h"
|
||||||
|
|
||||||
USTRUCT()
|
UCLASS()
|
||||||
struct INTEGRATION_API FTangibleManager
|
class INTEGRATION_API UlxTangibleManager : public UObject
|
||||||
{
|
{
|
||||||
GENERATED_BODY()
|
GENERATED_BODY()
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Import these types into our Namespace.
|
// Types used frequently.
|
||||||
using IdArray = CommonTypes::IdArray;
|
|
||||||
using IdView = CommonTypes::IdView;
|
using IdView = CommonTypes::IdView;
|
||||||
|
using IdArray = CommonTypes::IdArray;
|
||||||
|
using TanArray = TArray<UlxTangible*>;
|
||||||
|
|
||||||
// A pointer to the UWorld.
|
// A pointer to our world.
|
||||||
UWorld* World;
|
UPROPERTY()
|
||||||
|
TWeakObjectPtr<UWorld> World;
|
||||||
|
|
||||||
// A pointer to uclass TangibleActor.
|
// A pointer to uclass TangibleActor. This is the class
|
||||||
|
// of actors that we create (for now).
|
||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
TSubclassOf<AActor> ClassTangibleActor;
|
TSubclassOf<AActor> ClassTangibleActor;
|
||||||
|
|
||||||
// Given a tangible ID, look up actor pointer (or NULL if actor was deleted)
|
// Given a tangible ID, look up the TangibleComponent of that actor.
|
||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
TMap<int64, UlxTangible*> IdToTangible;
|
TMap<int64, UlxTangible*> IdToTangible;
|
||||||
|
|
||||||
// Actor tangible Id.
|
|
||||||
int64 Actor;
|
|
||||||
|
|
||||||
// Tangibles near the actor.
|
|
||||||
IdView Near;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
FTangibleManager();
|
UlxTangibleManager();
|
||||||
|
|
||||||
// Initialize the tangible manager.
|
// Initialize the tangible manager.
|
||||||
//
|
//
|
||||||
void Init(UWorld *world, UClass* tanact);
|
void Init(UWorld *world, UClass* tanact);
|
||||||
|
|
||||||
|
// Get a pointer to our world.
|
||||||
|
//
|
||||||
|
UWorld* GetWorld() const override { return World.Get(); }
|
||||||
|
|
||||||
// Get the tangible if it exists, otherwise return NULL
|
// Get the tangible if it exists, otherwise return NULL
|
||||||
UlxTangible* GetTangible(int64 id);
|
//
|
||||||
|
UlxTangible* GetTangible(int64 id) const;
|
||||||
|
|
||||||
// Get the tangible if it exists, otherwise create it.
|
// Get the tangible if it exists, otherwise create it.
|
||||||
|
//
|
||||||
UlxTangible* MakeTangible(int64 id);
|
UlxTangible* MakeTangible(int64 id);
|
||||||
|
|
||||||
// Delete the tangible.
|
// Delete the tangible.
|
||||||
|
//
|
||||||
void DeleteTangible(int64 id);
|
void DeleteTangible(int64 id);
|
||||||
|
|
||||||
// Get/Set the Id of the actor.
|
// Get an array of all tangibles.
|
||||||
//
|
//
|
||||||
int64 GetActor() const { return Actor; };
|
TanArray GetAllTangibles() const;
|
||||||
void SetActor(int64 id) { Actor = id; }
|
|
||||||
|
|
||||||
// Get/Set the list of tangibles near the player, according to Luprex.
|
// Update the 'NearAccordingToLuprex' flags.
|
||||||
//
|
//
|
||||||
IdView GetNear() const { return Near; }
|
// Also creates stub tangibles for every Id in the list.
|
||||||
void SetNear(IdView near) { Near = near; }
|
//
|
||||||
|
void UpdateNearAccordingToLuprex(IdView near);
|
||||||
|
|
||||||
// Get the Live list.
|
// Recalculate the 'NearAccordingToUnreal' flags.
|
||||||
//
|
//
|
||||||
// Efficiency note: this makes a copy of the array.
|
void RecalcNearAccordingToUnreal(int64 player, double radius);
|
||||||
|
|
||||||
|
// Delete Far Tangibles.
|
||||||
//
|
//
|
||||||
IdArray GetLive();
|
// Any tangible whose 'NearAccordingToLuprex' and 'NearAccordingToUnreal'
|
||||||
|
// flags are both false is deleted. You probably want to update both
|
||||||
|
// flags by calling the two routines above before calling this.
|
||||||
|
//
|
||||||
|
void DeleteFarawayTangibles();
|
||||||
|
|
||||||
|
// Given an array of tangibles, return an array of tangible Ids.
|
||||||
|
//
|
||||||
|
static IdArray GetIds(const TanArray &tans);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
#include "c:/luprex/ext/base-writer.hpp"
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
#include "c:/luprex/cpp/drv/drvutil.cpp"
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
#include "c:/luprex/cpp/drv/drvutil.hpp"
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
#include "c:/luprex/cpp/core/enginewrapper.hpp"
|
|
||||||
27
luprex-install.sh
Executable file
27
luprex-install.sh
Executable file
@@ -0,0 +1,27 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# Install symbolic links to the Luprex source and the Luprex DLL.
|
||||||
|
#
|
||||||
|
# These symbolic links are needed to be able to build integration.
|
||||||
|
#
|
||||||
|
#
|
||||||
|
|
||||||
|
if [ -d c:/luprex ] ; then
|
||||||
|
LUPREX=c:/luprex
|
||||||
|
DLL=$LUPREX/build/visual/luprexlib.dll
|
||||||
|
else
|
||||||
|
LUPREX=$HOME/luprex
|
||||||
|
DLL=$LUPREX/build/linux/luprexlib.so
|
||||||
|
fi
|
||||||
|
|
||||||
|
rm -f Source/Integration/lpx-*.hpp
|
||||||
|
rm -f Source/Integration/lpx-*.cpp
|
||||||
|
|
||||||
|
echo '#include "'$LUPREX'/ext/base-buffer.hpp"' > Source/Integration/lpx-basebuffer.hpp
|
||||||
|
echo '#include "'$LUPREX'/cpp/drv/drvutil.hpp"' > Source/Integration/lpx-drvutil.hpp
|
||||||
|
echo '#include "'$LUPREX'/cpp/drv/drvutil.cpp"' > Source/Integration/lpx-drvutil.cpp
|
||||||
|
echo '#include "'$LUPREX'/cpp/core/enginewrapper.hpp"' > Source/Integration/lpx-enginewrapper.hpp
|
||||||
|
echo '#define LUPREX_DLL_PATH "'$DLL'"' > Source/Integration/lpx-paths.hpp
|
||||||
|
echo '#define LUPREX_ROOT_PATH "'$LUPREX'"' >> Source/Integration/lpx-paths.hpp
|
||||||
|
|
||||||
Reference in New Issue
Block a user