// Erm, I think this fudge works FProcMeshTangent FZoneGeneratorWorker::GetTangentFromNormal(const FVector aNormal) { FVector tangentVec, bitangentVec; FVector c1, c2; c1 = FVector::CrossProduct(aNormal, FVector(0.0f, 0.0f, 1.0f)); c2 = FVector::CrossProduct(aNormal, FVector(0.0f, 1.0f, 0.0f)); if (c1.Size() > c2.Size()) { tangentVec = c1; } else { tangentVec = c2; } tangentVec = tangentVec.GetSafeNormal(); bitangentVec = FVector::CrossProduct(aNormal, tangentVec); return FProcMeshTangent(bitangentVec, false ); }
// Sets default values AMyObjPawn::AMyObjPawn() { // Set this pawn to call Tick() every frame. You can turn this off to improve performance if you don't need it. PrimaryActorTick.bCanEverTick = true; // Set this pawn to be controlled by the lowest-numbered player (このポーンが最小値のプレイヤーで制御されるように設定) AutoPossessPlayer = EAutoReceiveInput::Player0; // ダミーキャラクターを置く RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("RootComponent")); // Create a dummy root component we can attach things to.(親子付け可能なダミーのルートコンポーネントを作成) UCameraComponent* OurCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("OurCamera")); // Attach our camera and visible object to our root component. (カメラと可視オブジェクトをルートコンポーネントに親子付け。カメラをオフセットして回転) OurCamera->AttachTo(RootComponent); OurCamera->SetRelativeLocation(FVector(-350.0f, 0.0f, 100.0f)); OurCamera->SetRelativeRotation(FRotator(0.0f, 0.0f, 0.0f)); /** * Create/replace a section for this procedural mesh component. * @param SectionIndex Index of the section to create or replace. * @param Vertices Vertex buffer of all vertex positions to use for this mesh section. * @param Triangles Index buffer indicating which vertices make up each triangle. Length must be a multiple of 3. * @param Normals Optional array of normal vectors for each vertex. If supplied, must be same length as Vertices array. * @param UV0 Optional array of texture co-ordinates for each vertex. If supplied, must be same length as Vertices array. * @param VertexColors Optional array of colors for each vertex. If supplied, must be same length as Vertices array. * @param Tangents Optional array of tangent vector for each vertex. If supplied, must be same length as Vertices array. * @param bCreateCollision Indicates whether collision should be created for this section. This adds significant cost. */ // 動的オブジェクトを置く mProceduralMeshComponent = CreateDefaultSubobject<UProceduralMeshComponent>(TEXT("GeneratedMesh")); TArray<FVector> positions; TArray<FVector> normals; TArray<FVector2D> uvs; TArray<FColor> colors; TArray<FProcMeshTangent> tangents; TArray<int32> indices; ObjectParser(positions, normals, uvs, indices); for (size_t i = 0; i < positions.Num(); i++) { colors.Add(FColor(255, 255, 255, 255)); tangents.Add(FProcMeshTangent(1, 1, 1)); } ConstructorHelpers::FObjectFinder<UMaterial>* pMaterialAsset = new ConstructorHelpers::FObjectFinder<UMaterial>( _T("/Game/InfinityBladeAdversaries/Enemy/Enemy_Bear/Materials/M_Bear_Master.M_Bear_Master") ); if (pMaterialAsset->Succeeded()) { mMaterial = pMaterialAsset->Object; UE_LOG(LogTemp, Warning, TEXT("output : %s"), L"マテリアルロードに成功しました"); } else { UE_LOG(LogTemp, Warning, TEXT("output : %s"), L"マテリアルロードに失敗しました"); } mProceduralMeshComponent->CreateMeshSection(0, positions, indices, normals, uvs, colors, tangents, false); mProceduralMeshComponent->SetMaterial(0, mMaterial); mProceduralMeshComponent->AttachTo(RootComponent); }
int32 FProceduralTerrainWorker::PolygonizeToTriangles( UTerrainGrid *TerrainGrid, float p_fSurfaceCrossValue, FIntVector ChunkStartSize, FIntVector ChunkEndSize, FVector &ChunkLocation, FVector &ChunkScale, FTransform &TerrainTransform, TArray<FVector> &Vertices, TArray<int32> &Indices, TArray<FVector> &Normals, TArray<FVector2D> &UVs, TArray<FColor> &VertexColors, TArray<FProcMeshTangent> &Tangents ) { // TODO? float PosX = ChunkLocation.X; float PosY = ChunkLocation.Y; float PosZ = ChunkLocation.Z; TArray<FVector> Positions; int NumTriangles = 0; for (int32 x = ChunkStartSize.X; x < ChunkEndSize.X; ++x) { for (int32 y = ChunkStartSize.Y; y < ChunkEndSize.Y; ++y) { for (int32 z = ChunkStartSize.Z; z < ChunkEndSize.Z; ++z) { // Get each points of a cube. float p0 = TerrainGrid->GetVoxel(x, y, z); float p1 = TerrainGrid->GetVoxel(x + 1, y, z); float p2 = TerrainGrid->GetVoxel(x, y + 1, z); float p3 = TerrainGrid->GetVoxel(x + 1, y + 1, z); float p4 = TerrainGrid->GetVoxel(x, y, z + 1); float p5 = TerrainGrid->GetVoxel(x + 1, y, z + 1); float p6 = TerrainGrid->GetVoxel(x, y + 1, z + 1); float p7 = TerrainGrid->GetVoxel(x + 1, y + 1, z + 1); /* Determine the index into the edge table which tells us which vertices are inside of the surface */ int crossBitMap = 0; if (p0 < p_fSurfaceCrossValue) crossBitMap |= 1; if (p1 < p_fSurfaceCrossValue) crossBitMap |= 2; if (p2 < p_fSurfaceCrossValue) crossBitMap |= 8; if (p3 < p_fSurfaceCrossValue) crossBitMap |= 4; if (p4 < p_fSurfaceCrossValue) crossBitMap |= 16; if (p5 < p_fSurfaceCrossValue) crossBitMap |= 32; if (p6 < p_fSurfaceCrossValue) crossBitMap |= 128; if (p7 < p_fSurfaceCrossValue) crossBitMap |= 64; /* Cube is entirely in/out of the surface */ int edgeBits = edgeTable[crossBitMap]; if (edgeBits == 0) continue; float interpolatedCrossingPoint = 0.0f; FVector interpolatedValues[12]; if ((edgeBits & 1) > 0) { interpolatedCrossingPoint = (p_fSurfaceCrossValue - p0) / (p1 - p0); interpolatedValues[0] = FMath::Lerp(FVector(PosX + x, PosY + y, PosZ + z), FVector(PosX + x + 1, PosY + y, PosZ + z), interpolatedCrossingPoint); } if ((edgeBits & 2) > 0) { interpolatedCrossingPoint = (p_fSurfaceCrossValue - p1) / (p3 - p1); interpolatedValues[1] = FMath::Lerp(FVector(PosX + x + 1, PosY + y, PosZ + z), FVector(PosX + x + 1, PosY + y + 1, PosZ + z), interpolatedCrossingPoint); } if ((edgeBits & 4) > 0) { interpolatedCrossingPoint = (p_fSurfaceCrossValue - p2) / (p3 - p2); interpolatedValues[2] = FMath::Lerp(FVector(PosX + x, PosY + y + 1, PosZ + z), FVector(PosX + x + 1, PosY + y + 1, PosZ + z), interpolatedCrossingPoint); } if ((edgeBits & 8) > 0) { interpolatedCrossingPoint = (p_fSurfaceCrossValue - p0) / (p2 - p0); interpolatedValues[3] = FMath::Lerp(FVector(PosX + x, PosY + y, PosZ + z), FVector(PosX + x, PosY + y + 1, PosZ + z), interpolatedCrossingPoint); } //Top four edges if ((edgeBits & 16) > 0) { interpolatedCrossingPoint = (p_fSurfaceCrossValue - p4) / (p5 - p4); interpolatedValues[4] = FMath::Lerp(FVector(PosX + x, PosY + y, PosZ + z + 1), FVector(PosX + x + 1, PosY + y, PosZ + z + 1), interpolatedCrossingPoint); } if ((edgeBits & 32) > 0) { interpolatedCrossingPoint = (p_fSurfaceCrossValue - p5) / (p7 - p5); interpolatedValues[5] = FMath::Lerp(FVector(PosX + x + 1, PosY + y, PosZ + z + 1), FVector(PosX + x + 1, PosY + y + 1, PosZ + z + 1), interpolatedCrossingPoint); } if ((edgeBits & 64) > 0) { interpolatedCrossingPoint = (p_fSurfaceCrossValue - p6) / (p7 - p6); interpolatedValues[6] = FMath::Lerp(FVector(PosX + x, PosY + y + 1, PosZ + z + 1), FVector(PosX + x + 1, PosY + y + 1, PosZ + z + 1), interpolatedCrossingPoint); } if ((edgeBits & 128) > 0) { interpolatedCrossingPoint = (p_fSurfaceCrossValue - p4) / (p6 - p4); interpolatedValues[7] = FMath::Lerp(FVector(PosX + x, PosY + y, PosZ + z + 1), FVector(PosX + x, PosY + y + 1, PosZ + z + 1), interpolatedCrossingPoint); } //Side four edges if ((edgeBits & 256) > 0) { interpolatedCrossingPoint = (p_fSurfaceCrossValue - p0) / (p4 - p0); interpolatedValues[8] = FMath::Lerp(FVector(PosX + x, PosY + y, PosZ + z), FVector(PosX + x, PosY + y, PosZ + z + 1), interpolatedCrossingPoint); } if ((edgeBits & 512) > 0) { interpolatedCrossingPoint = (p_fSurfaceCrossValue - p1) / (p5 - p1); interpolatedValues[9] = FMath::Lerp(FVector(PosX + x + 1, PosY + y, PosZ + z), FVector(PosX + x + 1, PosY + y, PosZ + z + 1), interpolatedCrossingPoint); } if ((edgeBits & 1024) > 0) { interpolatedCrossingPoint = (p_fSurfaceCrossValue - p3) / (p7 - p3); interpolatedValues[10] = FMath::Lerp(FVector(PosX + x + 1, PosY + y + 1, PosZ + z), FVector(PosX + x + 1, PosY + y + 1, PosZ + z + 1), interpolatedCrossingPoint); } if ((edgeBits & 2048) > 0) { interpolatedCrossingPoint = (p_fSurfaceCrossValue - p2) / (p6 - p2); interpolatedValues[11] = FMath::Lerp(FVector(PosX + x, PosY + y + 1, PosZ + z), FVector(PosX + x, PosY + y + 1, PosZ + z + 1), interpolatedCrossingPoint); } crossBitMap <<= 4; int triangleIndex = 0; while (triTable[crossBitMap + triangleIndex] != -1) { // For each triangle in the look up table, create a triangle and add it to the list. int index1 = triTable[crossBitMap + triangleIndex]; int index2 = triTable[crossBitMap + triangleIndex + 1]; int index3 = triTable[crossBitMap + triangleIndex + 2]; FDynamicMeshVertex Vertex0; Vertex0.Position = TerrainTransform.TransformVector(ChunkScale * interpolatedValues[index1]); FDynamicMeshVertex Vertex1; Vertex1.Position = TerrainTransform.TransformVector(ChunkScale * interpolatedValues[index2]); FDynamicMeshVertex Vertex2; Vertex2.Position = TerrainTransform.TransformVector(ChunkScale * interpolatedValues[index3]); Vertex0.TextureCoordinate.X = Vertex0.Position.X / 100.0f; Vertex0.TextureCoordinate.Y = Vertex0.Position.Y / 100.0f; Vertex1.TextureCoordinate.X = Vertex1.Position.X / 100.0f; Vertex1.TextureCoordinate.Y = Vertex1.Position.Y / 100.0f; Vertex2.TextureCoordinate.X = Vertex2.Position.X / 100.0f; Vertex2.TextureCoordinate.Y = Vertex2.Position.Y / 100.0f; // Fill Index buffer And Vertex buffer with the generated vertices. int32 VIndex0 = Positions.Find(Vertex0.Position); if (VIndex0 < 0) { VIndex0 = Positions.Add(Vertex0.Position); Indices.Add(VIndex0); Vertices.Add(Vertex0.Position); UVs.Add(FVector2D(Vertex0.TextureCoordinate)); } else { Indices.Add(VIndex0); } int32 VIndex1 = Positions.Find(Vertex1.Position); if (VIndex1 < 0) { VIndex1 = Positions.Add(Vertex1.Position); Indices.Add(VIndex1); Vertices.Add(Vertex1.Position); UVs.Add(FVector2D(Vertex1.TextureCoordinate)); } else { Indices.Add(VIndex1); } int32 VIndex2 = Positions.Find(Vertex2.Position); if (VIndex2 < 0) { VIndex2 = Positions.Add(Vertex2.Position); Indices.Add(VIndex2); Vertices.Add(Vertex2.Position); UVs.Add(FVector2D(Vertex2.TextureCoordinate)); } else { Indices.Add(VIndex2); } ++NumTriangles; triangleIndex += 3; } } } } VertexColors.SetNum(Vertices.Num(), false); Normals.SetNum(Vertices.Num(), false); Tangents.SetNum(Vertices.Num(), false); for (int32 Index = 0; Index < Indices.Num(); Index += 3) { const FVector Edge21 = Vertices[Indices[Index + 1]] - Vertices[Indices[Index + 2]]; const FVector Edge20 = Vertices[Indices[Index + 0]] - Vertices[Indices[Index + 2]]; FVector TriNormal = (Edge21 ^ Edge20).GetSafeNormal(); FVector FaceTangentX = Edge20.GetSafeNormal(); FVector FaceTangentY = (FaceTangentX ^ TriNormal).GetSafeNormal(); FVector FaceTangentZ = TriNormal; // Use Gram-Schmidt orthogonalization to make sure X is orth with Z FaceTangentX -= FaceTangentZ * (FaceTangentZ | FaceTangentX); FaceTangentX.Normalize(); // See if we need to flip TangentY when generating from cross product const bool bFlipBitangent = ((FaceTangentZ ^ FaceTangentX) | FaceTangentY) < 0.f; TriNormal.Normalize(); FaceTangentX.Normalize(); FProcMeshTangent tangent = FProcMeshTangent(FaceTangentX, bFlipBitangent); for (int32 i = 0; i < 3; i++) { Normals[Indices[Index + i]] = TriNormal; Tangents[Indices[Index + i]] = tangent; VertexColors[Indices[Index + i]] = FColor(1, 1, 1, 1); } } //UE_LOG(LogTemp, Warning, TEXT("NumTriangles: %d => %d,%d -> %d,%d"), NumTriangles, ChunkStartSize.X, ChunkStartSize.Y, ChunkEndSize.X, ChunkEndSize.Y); return NumTriangles; }
void ASimpleCubeActor::GenerateCube(FProceduralMeshData& MeshData, float Depth, float Width, float Height) { // NOTE: Unreal uses an upper-left origin UV // NOTE: This sample uses a simple UV mapping scheme where each face is the same // NOTE: For a normal facing towards us, be build the polygon CCW in the order 0-1-2 then 0-2-3 to complete the quad. // Remember in Unreal, X is forwards, Y is to your right and Z is up. // Calculate a half offset so we get correct center of object float DepthOffset = Depth / 2.0f; // X float WidthOffset = Width / 2.0f; // Y float HeightOffset = Height / 2.0f; // Z // Define the 8 corners of the cube FVector p0 = FVector(DepthOffset, WidthOffset, -HeightOffset); FVector p1 = FVector(DepthOffset, -WidthOffset, -HeightOffset); FVector p2 = FVector(DepthOffset, -WidthOffset, HeightOffset); FVector p3 = FVector(DepthOffset, WidthOffset, HeightOffset); FVector p4 = FVector(-DepthOffset, WidthOffset, -HeightOffset); FVector p5 = FVector(-DepthOffset, -WidthOffset, -HeightOffset); FVector p6 = FVector(-DepthOffset, -WidthOffset, HeightOffset); FVector p7 = FVector(-DepthOffset, WidthOffset, HeightOffset); // Now we create 6x faces, 4 vertices each int32 VertexCount = 6 * 4; MeshData.Vertices.AddUninitialized(VertexCount); MeshData.UVs.AddUninitialized(VertexCount); MeshData.Normals.AddUninitialized(VertexCount); MeshData.Tangents.AddUninitialized(VertexCount); int32 VertexOffset = 0; FVector Normal = FVector::ZeroVector; FProcMeshTangent Tangent = FProcMeshTangent(); // Front (+X) face: 0-1-2-3 Normal = FVector(1, 0, 0); Tangent = FProcMeshTangent(0, 1, 0); VertexOffset = BuildQuad(MeshData, p0, p1, p2, p3, VertexOffset, Normal, Tangent); // Back (-X) face: 5-4-7-6 Normal = FVector(-1, 0, 0); Tangent = FProcMeshTangent(0, -1, 0); VertexOffset = BuildQuad(MeshData, p5, p4, p7, p6, VertexOffset, Normal, Tangent); // Left (-Y) face: 1-5-6-2 Normal = FVector(0, -1, 0); Tangent = FProcMeshTangent(1, 0, 0); VertexOffset = BuildQuad(MeshData, p1, p5, p6, p2, VertexOffset, Normal, Tangent); // Right (+Y) face: 4-0-3-7 Normal = FVector(0, 1, 0); Tangent = FProcMeshTangent(-1, 0, 0); VertexOffset = BuildQuad(MeshData, p4, p0, p3, p7, VertexOffset, Normal, Tangent); // Top (+Z) face: 6-7-3-2 Normal = FVector(0, 0, 1); Tangent = FProcMeshTangent(0, 1, 0); VertexOffset = BuildQuad(MeshData, p6, p7, p3, p2, VertexOffset, Normal, Tangent); // Bottom (-Z) face: 1-0-4-5 Normal = FVector(0, 0, -1); Tangent = FProcMeshTangent(0, -1, 0); VertexOffset = BuildQuad(MeshData, p1, p0, p4, p5, VertexOffset, Normal, Tangent); }