void AFlarePlanetarium::BeginPlay() { Super::BeginPlay(); FLOG("AFlarePlanetarium::BeginPlay"); TArray<UActorComponent*> Components = GetComponentsByClass(UStaticMeshComponent::StaticClass()); for (int32 ComponentIndex = 0; ComponentIndex < Components.Num(); ComponentIndex++) { UStaticMeshComponent* PlanetCandidate = Cast<UStaticMeshComponent>(Components[ComponentIndex]); if (PlanetCandidate) { // Apply a new dynamic material to planets so that we can control shading parameters UMaterialInstanceConstant* BasePlanetMaterial = Cast<UMaterialInstanceConstant>(PlanetCandidate->GetMaterial(0)); if (BasePlanetMaterial) { //FLOGV("AFlarePlanetarium::BeginPlay : found planet '%s'", *PlanetCandidate->GetName()); #if PLATFORM_LINUX int32 UseNormalAsLightingDirection = 1; #else int32 UseNormalAsLightingDirection = 0; #endif UMaterialInstanceDynamic* PlanetMaterial = UMaterialInstanceDynamic::Create(BasePlanetMaterial, GetWorld()); PlanetCandidate->SetMaterial(0, PlanetMaterial); PlanetMaterial->SetScalarParameterValue("UseNormalAsLightingDirection", UseNormalAsLightingDirection); } } } }
void AFlarePlanetarium::SetupCelestialBody(CelestialBodyPosition* BodyPosition, double DisplayDistance, double DisplayRadius) { FVector PlayerShipLocation = FVector::ZeroVector; if (GetGame()->GetPC()->GetShipPawn()) { PlayerShipLocation = GetGame()->GetPC()->GetShipPawn()->GetActorLocation(); } #ifdef PLANETARIUM_DEBUG DrawDebugSphere(GetWorld(), FVector::ZeroVector, DisplayDistance /1000 , 32, FColor::Blue, false); PlayerShipLocation = FVector::ZeroVector; DisplayRadius /= 1000; DisplayDistance /= 1000; #endif BodyPosition->BodyComponent->SetRelativeLocation((DisplayDistance * BodyPosition->AlignedLocation.GetUnsafeNormal()).ToVector() + PlayerShipLocation); float Scale = DisplayRadius / 512; // Mesh size is 1024; BodyPosition->BodyComponent->SetRelativeScale3D(FPreciseVector(Scale).ToVector()); FTransform BaseRotation = FTransform(FRotator(0, 0 ,90)); FTransform TimeRotation = FTransform(FRotator(0, BodyPosition->TotalRotation, 0)); FQuat Rotation = (TimeRotation * BaseRotation).GetRotation(); // TODO Rotation float time interpolation BodyPosition->BodyComponent->SetRelativeRotation(FQuat::Identity); BodyPosition->BodyComponent->SetRelativeRotation(Rotation); // Apply sun direction to component UMaterialInstanceDynamic* ComponentMaterial = Cast<UMaterialInstanceDynamic>(BodyPosition->BodyComponent->GetMaterial(0)); if (!ComponentMaterial) { ComponentMaterial = UMaterialInstanceDynamic::Create(BodyPosition->BodyComponent->GetMaterial(0) , GetWorld()); BodyPosition->BodyComponent->SetMaterial(0, ComponentMaterial); } ComponentMaterial->SetVectorParameterValue("SunDirection", SunDirection.ToVector()); // Look for rings and orient them TArray<USceneComponent*> RingCandidates; BodyPosition->BodyComponent->GetChildrenComponents(true, RingCandidates); for (int32 ComponentIndex = 0; ComponentIndex < RingCandidates.Num(); ComponentIndex++) { UStaticMeshComponent* RingComponent = Cast<UStaticMeshComponent>(RingCandidates[ComponentIndex]); if (RingComponent && RingComponent->GetName().Contains("ring")) { // Get or create the material UMaterialInstanceDynamic* RingMaterial = Cast<UMaterialInstanceDynamic>(RingComponent->GetMaterial(0)); if (!RingMaterial) { RingMaterial = UMaterialInstanceDynamic::Create(RingComponent->GetMaterial(0), GetWorld()); RingComponent->SetMaterial(0, RingMaterial); } // Get world-space rotation angles for the ring and the sun float SunRotationPitch = FMath::RadiansToDegrees(FMath::Atan2(SunDirection.Z,SunDirection.X)) + 180; float RingRotationPitch = -BodyPosition->TotalRotation; // Feed params to the shader RingMaterial->SetScalarParameterValue("RingPitch", RingRotationPitch / 360); RingMaterial->SetScalarParameterValue("SunPitch", SunRotationPitch / 360); } } // Sun also rotates to track direction if (BodyPosition->Body == &Sun) { BodyPosition->BodyComponent->SetRelativeRotation(SunDirection.ToVector().Rotation()); } // Compute sun occlusion if (BodyPosition->Body != &Sun) { double OcclusionAngle = FPreciseMath::Asin(BodyPosition->Radius / BodyPosition->Distance); float BodyPhase = FMath::UnwindRadians(FMath::Atan2(BodyPosition->AlignedLocation.Z, BodyPosition->AlignedLocation.X)); float CenterAngularDistance = FMath::Abs(FMath::UnwindRadians(SunPhase - BodyPhase)); float AngleSum = (SunOcclusionAngle + OcclusionAngle); float AngleDiff = FMath::Abs(SunOcclusionAngle - OcclusionAngle); /*FLOGV("SetupCelestialBody %s BodyPhase = %f", *BodyPosition->Body->Name.ToString(), FMath::RadiansToDegrees(BodyPhase)); FLOGV("SetupCelestialBody %s SunPhase = %f", *BodyPosition->Body->Name.ToString(), FMath::RadiansToDegrees(SunPhase)); FLOGV("SetupCelestialBody %s OcclusionAngle = %f", *BodyPosition->Body->Name.ToString(), FMath::RadiansToDegrees(OcclusionAngle)); FLOGV("SetupCelestialBody %s SunOcclusionAngle = %f", *BodyPosition->Body->Name.ToString(), FMath::RadiansToDegrees(SunOcclusionAngle)); FLOGV("SetupCelestialBody %s AngleDiff = %f", *BodyPosition->Body->Name.ToString(), FMath::RadiansToDegrees(AngleDiff)); FLOGV("SetupCelestialBody %s CenterAngularDistance = %f", *BodyPosition->Body->Name.ToString(), FMath::RadiansToDegrees(CenterAngularDistance)); FLOGV("SetupCelestialBody %s AngleSum = %f", *BodyPosition->Body->Name.ToString(), FMath::RadiansToDegrees(AngleSum));*/ if (CenterAngularDistance < AngleSum) { // There is occlusion float OcclusionRatio; if (CenterAngularDistance < AngleDiff) { // Maximum occlusion OcclusionRatio = 1.0; } else { // Partial occlusion OcclusionRatio = (AngleSum - CenterAngularDistance) / (2* FMath::Min(SunOcclusionAngle, OcclusionAngle)); // OcclusionRatio = ((SunOcclusionAngle + OcclusionAngle) + FMath::Max(SunOcclusionAngle, OcclusionAngle) - FMath::Min(SunOcclusionAngle, OcclusionAngle)) / (2 * CenterAngularDistance); } //FLOGV("MoveCelestialBody %s OcclusionRatio = %f", *Body->Name, OcclusionRatio); // Now, find the surface occlusion float SunAngularSurface = PI*FMath::Square(SunOcclusionAngle); float MaxOcclusionAngularSurface = PI*FMath::Square(FMath::Min(SunOcclusionAngle, OcclusionAngle)); float MaxOcclusion = MaxOcclusionAngularSurface / SunAngularSurface; float Occlusion = OcclusionRatio * MaxOcclusion; /*FLOGV("SetupCelestialBody %s CenterAngularDistance = %f", *BodyPosition->Body->Name.ToString(), CenterAngularDistance); FLOGV("SetupCelestialBody %s SunOcclusionAngle = %f", *BodyPosition->Body->Name.ToString(), SunOcclusionAngle); FLOGV("SetupCelestialBody %s OcclusionAngle = %f", *BodyPosition->Body->Name.ToString(), OcclusionAngle); FLOGV("SetupCelestialBody %s SunAngularSurface = %f", *BodyPosition->Body->Name.ToString(), SunAngularSurface); FLOGV("SetupCelestialBody %s MaxOcclusionAngularSurface = %f", *BodyPosition->Body->Name.ToString(), MaxOcclusionAngularSurface); FLOGV("SetupCelestialBody %s MaxOcclusion = %f", *BodyPosition->Body->Name.ToString(), MaxOcclusion); FLOGV("SetupCelestialBody %s Occlusion = %f", *BodyPosition->Body->Name.ToString(), Occlusion);*/ if (Occlusion > SunOcclusion) { // Keep only best occlusion SunOcclusion = Occlusion; } } } }