예제 #1
0
파일: Mesh.cpp 프로젝트: ezEngine/ezEngine
  ezResult Mesh::ComputeNormals()
  {
    ezStopwatch timer;

    const VertexDataStream* positionStreamRaw = GetDataStream(ezGALVertexAttributeSemantic::Position);
    if (positionStreamRaw == nullptr)
    {
      ezLog::Error("Can't compute vertex normals for the mesh '{0}', because it doesn't have vertex positions.", m_Name);
      return EZ_FAILURE;
    }
    const TypedVertexDataStreamView<ezVec3> positionStream(*positionStreamRaw);

    VertexDataStream* normalStreamRaw = AddDataStream(ezGALVertexAttributeSemantic::Normal, 3);
    TypedVertexDataStreamView_ReadWrite<ezVec3> normalStream(*normalStreamRaw);

    // Normals have same mapping as positions.
    normalStream->m_IndexToData = positionStreamRaw->m_IndexToData;
    // Reset all normals to zero.
    normalStream->m_Data.SetCountUninitialized(positionStreamRaw->m_Data.GetCount());
    ezMemoryUtils::ZeroFill<char>(normalStream->m_Data.GetData(), normalStream->m_Data.GetCount());


    // Compute unnormalized triangle normals and add them to all vertices.
    // This way large triangles have an higher influence on the vertex normal.
    for (const Triangle& triangle : m_Triangles)
    {
      const VertexIndex v0 = triangle.m_Vertices[0];
      const VertexIndex v1 = triangle.m_Vertices[1];
      const VertexIndex v2 = triangle.m_Vertices[2];

      const ezVec3 p0 = positionStream.GetValue(v0);
      const ezVec3 p1 = positionStream.GetValue(v1);
      const ezVec3 p2 = positionStream.GetValue(v2);

      const ezVec3 d01 = p1 - p0;
      const ezVec3 d02 = p2 - p0;

      const ezVec3 triNormal = d01.CrossRH(d02);
      normalStream.SetValue(v0, normalStream.GetValue(v0) +
                                    triNormal); // (possible optimization: have a special addValue to avoid unnecessary lookup)
      normalStream.SetValue(v1, normalStream.GetValue(v1) + triNormal);
      normalStream.SetValue(v2, normalStream.GetValue(v2) + triNormal);
    }

    // Normalize normals.
    for (ezUInt32 n = 0; n < normalStream->m_Data.GetCount(); n += sizeof(ezVec3))
      reinterpret_cast<ezVec3*>(&normalStream->m_Data[n])->NormalizeIfNotZero();

    ezLog::Debug("Computed mesh normals ('{0}') in '{1}'s", m_Name, ezArgF(timer.GetRunningTotal().GetSeconds(), 2));
    return EZ_SUCCESS;
  }
예제 #2
0
  ezSharedPtr<Scene> Importer::ImportScene(const char* szFileName, ezBitflags<ImportFlags> importFlags, bool addToCache)
  {
    ezLogBlock logBlock("Scene Import", szFileName);

    ezSharedPtr<Scene> scene;
    if (m_cachedScenes.TryGetValue(szFileName, scene))
      return scene;

    ezString fileExtension = ezPathUtils::GetFileExtension(szFileName);
    if (fileExtension.IsEmpty())
    {
      ezLog::Error("Unable to choose model importer since file '{0}' has no file extension.", szFileName);
      return nullptr;
    }

    // Newer implementations have higher priority.
    for (int i = m_ImporterImplementations.GetCount() - 1; i >= 0; --i)
    {
      auto supportedFormats = m_ImporterImplementations[i]->GetSupportedFileFormats();
      if (std::any_of(cbegin(supportedFormats), cend(supportedFormats),
                      [fileExtension](const char* ext) { return ezStringUtils::IsEqual_NoCase(ext, fileExtension); }))
      {
        ezStopwatch timer;
        scene = m_ImporterImplementations[i]->ImportScene(szFileName, importFlags);

#if EZ_ENABLED(EZ_COMPILE_FOR_DEBUG)
        ValidateSceneGraph(scene);
#endif
        if (scene)
        {
          scene->RefreshRootList();
          scene->CreateUniqueNames();
        }

        ezLog::Success("Imported scene '{0}' in {1} seconds", szFileName, ezArgF(timer.GetRunningTotal().GetSeconds(), 2));
        if (addToCache)
          m_cachedScenes.Insert(szFileName, scene);

        return scene;
      }
    }

    return nullptr;
  }
예제 #3
0
파일: Mesh.cpp 프로젝트: ezEngine/ezEngine
  ezResult Mesh::ComputeTangents()
  {
    ezStopwatch timer;

    struct MikkInterfaceImpl
    {
      MikkInterfaceImpl(Mesh& mesh, const VertexDataStream& position, const VertexDataStream& normal, const VertexDataStream& tex)
          : triangles(mesh.m_Triangles)
          , positionStream(position)
          , normalStream(normal)
          , texStream(tex)
          , tangentStream(*mesh.AddDataStream(ezGALVertexAttributeSemantic::Tangent, 3)) // Make sure tangent stream exists.
          , bitangentStream(*mesh.AddDataStream(ezGALVertexAttributeSemantic::BiTangent, 1))

          , bitangentIndexNegative(0)
          , bitangentIndexPositive(sizeof(float))
      {
        float biTangentSignValues[] = {-1.0f, 1.0f};
        bitangentStream.AddValues(ezMakeArrayPtr(biTangentSignValues));
      }

      ezArrayPtr<Triangle> triangles;
      const TypedVertexDataStreamView<ezVec3> positionStream;
      const TypedVertexDataStreamView<ezVec3> normalStream;
      const TypedVertexDataStreamView<ezVec2> texStream;
      TypedVertexDataStreamView_ReadWrite<ezVec3> tangentStream;
      TypedVertexDataStreamView_ReadWrite<float> bitangentStream;
      VertexDataIndex bitangentIndexPositive;
      VertexDataIndex bitangentIndexNegative;

      ezHashTable<ezVec3, VertexDataIndex> tangentDataMap;

      int GetNumFaces() const { return triangles.GetCount(); }
      int GetNumVerticesOfFace(const int iFace) const { return 3; }

      void GetPosition(float fvPosOut[], const int iFace, const int iVert) const
      {
        ezVec3 p = positionStream.GetValue(triangles[iFace].m_Vertices[iVert]);
        fvPosOut[0] = p.x;
        fvPosOut[1] = p.y;
        fvPosOut[2] = p.z;
      }
      void GetNormal(float fvNormOut[], const int iFace, const int iVert) const
      {
        ezVec3 n = normalStream.GetValue(triangles[iFace].m_Vertices[iVert]);
        fvNormOut[0] = n.x;
        fvNormOut[1] = n.y;
        fvNormOut[2] = n.z;
      }
      void GetTexCoord(float fvTexcOut[], const int iFace, const int iVert) const
      {
        ezVec2 uv = texStream.GetValue(triangles[iFace].m_Vertices[iVert]);
        fvTexcOut[0] = uv.x;
        fvTexcOut[1] = uv.y;
      }

      void SetTSpaceBasic(const float fvTangent[], const float fSign, const int iFace, const int iVert)
      {
        // Need to reconstruct indexing using hashmap lookups.
        // (will get a call to SetTSpaceBasic for every triangle vertex, not for every data vertex.)
        VertexIndex v = triangles[iFace].m_Vertices[iVert];
        ezVec3 key = ezVec3(fvTangent[0], fvTangent[1], fvTangent[2]);
        VertexDataIndex existingTangentIndex;
        if (tangentDataMap.TryGetValue(key, existingTangentIndex))
        {
          tangentStream->SetDataIndex(v, existingTangentIndex);
        }
        else
        {
          tangentStream.SetValue(v, ezVec3(fvTangent[0], fvTangent[1], fvTangent[2]));
          tangentDataMap.Insert(key, tangentStream->GetDataIndex(v));
        }

        // For bitangent sign its easy: There are only 2 signs and we've set the data already.
        if (fSign >= 1.0f)
          bitangentStream->SetDataIndex(v, bitangentIndexPositive);
        else
          bitangentStream->SetDataIndex(v, bitangentIndexNegative);
      }
    };

    // If there is already a data stream with 3 component bitangents, remove it.
    {
      VertexDataStream* bitangents = GetDataStream(ezGALVertexAttributeSemantic::BiTangent);
      if (bitangents && bitangents->GetNumElementsPerVertex() != 1)
        RemoveDataStream(ezGALVertexAttributeSemantic::BiTangent);
    }

    const VertexDataStream* positionStream = GetDataStream(ezGALVertexAttributeSemantic::Position);
    if (positionStream == nullptr)
    {
      ezLog::Error("Can't compute vertex tangents for the mesh '{0}', because it doesn't have vertex positions.", m_Name);
      return EZ_FAILURE;
    }
    const VertexDataStream* normalStream = GetDataStream(ezGALVertexAttributeSemantic::Normal);
    if (normalStream == nullptr)
    {
      ezLog::Error("Can't compute tangents for the mesh '{0}', because it doesn't have vertex normals.", m_Name);
      return EZ_FAILURE;
    }
    const VertexDataStream* texStream = GetDataStream(ezGALVertexAttributeSemantic::TexCoord0);
    if (texStream == nullptr || texStream->GetNumElementsPerVertex() != 2)
    {
      ezLog::Error("Can't compute tangents for the mesh '{0}', because it doesn't have TexCoord0 stream with two components.", m_Name);
      return EZ_FAILURE;
    }

    MikkInterfaceImpl mikkInterface(*this, *positionStream, *normalStream, *texStream);

    // Use Morton S. Mikkelsen's tangent calculation.
    SMikkTSpaceContext context;
    SMikkTSpaceInterface functions;
    context.m_pUserData = &mikkInterface;
    context.m_pInterface = &functions;
    functions.m_getNumFaces = [](const SMikkTSpaceContext* pContext) {
      return static_cast<MikkInterfaceImpl*>(pContext->m_pUserData)->GetNumFaces();
    };
    functions.m_getNumVerticesOfFace = [](const SMikkTSpaceContext* pContext, const int iFace) {
      return static_cast<MikkInterfaceImpl*>(pContext->m_pUserData)->GetNumVerticesOfFace(iFace);
    };
    functions.m_getPosition = [](const SMikkTSpaceContext* pContext, float fvPosOut[], const int iFace, const int iVert) {
      return static_cast<MikkInterfaceImpl*>(pContext->m_pUserData)->GetPosition(fvPosOut, iFace, iVert);
    };
    functions.m_getNormal = [](const SMikkTSpaceContext* pContext, float fvPosOut[], const int iFace, const int iVert) {
      return static_cast<MikkInterfaceImpl*>(pContext->m_pUserData)->GetNormal(fvPosOut, iFace, iVert);
    };
    functions.m_getTexCoord = [](const SMikkTSpaceContext* pContext, float fvPosOut[], const int iFace, const int iVert) {
      return static_cast<MikkInterfaceImpl*>(pContext->m_pUserData)->GetTexCoord(fvPosOut, iFace, iVert);
    };
    functions.m_setTSpaceBasic = [](const SMikkTSpaceContext* pContext, const float fvTangent[], const float fSign, const int iFace,
                                    const int iVert) {
      return static_cast<MikkInterfaceImpl*>(pContext->m_pUserData)->SetTSpaceBasic(fvTangent, fSign, iFace, iVert);
    };
    functions.m_setTSpace = nullptr;

    if (!genTangSpaceDefault(&context))
    {
      ezLog::Error("Failed to compute compute tangents for the mesh {0}.", m_Name);
      return EZ_FAILURE;
    }

    ezLog::Debug("Computed mesh normals ('{0}') in '{1}'s", m_Name, ezArgF(timer.GetRunningTotal().GetSeconds(), 2));
    return EZ_SUCCESS;
  }
예제 #4
0
void ezQGridBarWidget::paintEvent(QPaintEvent* e)
{
  if (!MapFromSceneFunc.IsValid())
  {
    QWidget::paintEvent(e);
    return;
  }

  QPainter Painter(this);
  QPainter* painter = &Painter;
  painter->setRenderHint(QPainter::Antialiasing);
  painter->setRenderHint(QPainter::TextAntialiasing);
  painter->setRenderHint(QPainter::HighQualityAntialiasing);

  QRect areaRect = rect();

  // background
  painter->fillRect(areaRect, palette().button());
  painter->translate(0.5, 0.5);

  // render fine grid stop lines
  {
    double fSceneMinX, fSceneMaxX;
    ezWidgetUtils::ComputeGridExtentsX(m_viewportSceneRect, m_fFineGridStops, fSceneMinX, fSceneMaxX);
    fSceneMinX = ezMath::Max(fSceneMinX, 0.0);

    painter->setPen(palette().buttonText().color());

    ezHybridArray<QLine, 100> lines;

    // some overcompensation for the case that the GraphicsView displays a scrollbar at the side
    for (double x = fSceneMinX; x <= fSceneMaxX + m_fTextGridStops; x += m_fFineGridStops)
    {
      const QPoint pos = MapFromSceneFunc(QPointF(x, 0));

      QLine& l = lines.ExpandAndGetRef();
      l.setLine(pos.x(), areaRect.bottom() - 3, pos.x(), areaRect.bottom());
    }

    painter->drawLines(lines.GetData(), lines.GetCount());
  }

  // Grid Stop Value Text
  {
    double fSceneMinX, fSceneMaxX;
    ezWidgetUtils::ComputeGridExtentsX(m_viewportSceneRect, m_fTextGridStops, fSceneMinX, fSceneMaxX);
    fSceneMinX = ezMath::Max(fSceneMinX, 0.0);

    QTextOption textOpt(Qt::AlignCenter);
    QRectF textRect;

    painter->setPen(palette().buttonText().color());

    ezStringBuilder tmp;

    for (double x = fSceneMinX; x <= fSceneMaxX; x += m_fTextGridStops)
    {
      const QPoint pos = MapFromSceneFunc(QPointF(x, 0));

      textRect.setRect(pos.x() - 50, areaRect.top(), 99, areaRect.height());
      tmp.Format("{0}", ezArgF(x));

      painter->drawText(textRect, tmp.GetData(), textOpt);
    }
  }
}