Esempio n. 1
0
 BBox3f TriangleMeshFull::extract(size_t id, RTCTriangle* triangles_o, size_t& numTriangles, Vec3fa* positions_o, size_t& numVertices) const
 {
   BBox3f bounds = empty;
   if (motion.size()) 
   {
     for (size_t j=0; j<triangles.size(); j++) {
       const TriangleMeshFull::Triangle& tri = triangles[j];
       triangles_o[numTriangles++] = RTCTriangle((int)numVertices+2*tri.v0,(int)numVertices+2*tri.v1,(int)numVertices+2*tri.v2,(int)(0x80000000 | id),(int)j);
     }
     for (size_t j=0; j<position.size(); j++) {
       const Vector3f p = position[j];
       const Vector3f dpdt = motion[j];
       positions_o[numVertices++] = Vector3f(p.x,p.y,p.z);
       positions_o[numVertices++] = Vector3f(dpdt.x,dpdt.y,dpdt.z);
       bounds.grow(p);
       bounds.grow(p+dpdt);
     }
   }
   else
   {
     for (size_t j=0; j<triangles.size(); j++) {
       const TriangleMeshFull::Triangle& tri = triangles[j];
       triangles_o[numTriangles++] = RTCTriangle((int)numVertices+tri.v0,(int)numVertices+tri.v1,(int)numVertices+tri.v2,(int)id,(int)j);
     }
     for (size_t j=0; j<position.size(); j++) {
       const Vector3f p = position[j];
       positions_o[numVertices++] = Vector3f(p.x,p.y,p.z);
       bounds.grow(p);
     }
   }
   return bounds;
 }
Esempio n. 2
0
TEST(BBoxTest, mergedWithVec) {
    const BBox3f bounds(Vec3f(-12.0f, -3.0f, 4.0f), Vec3f(7.0f, 8.0f,  9.0f));
    const Vec3f  vec(-10.0f, -6.0f, 10.0f);
    const BBox3f merged(Vec3f(-12.0f, -6.0f, 4.0f), Vec3f(7.0f, 8.0f, 10.0f));
    
    ASSERT_EQ(merged, bounds.mergedWith(vec));
}
Esempio n. 3
0
  BBox3f TriangleMeshTriangle4::update(char* prim, size_t num, void* geom) const 
  {
    BBox3f bounds = empty;
    TriangleMeshScene::TriangleMesh* mesh = (TriangleMeshScene::TriangleMesh*) geom;
    
    for (size_t j=0; j<num; j++) 
    {
      Triangle4& dst = ((Triangle4*) prim)[j];
      
      ssei vgeomID = -1, vprimID = -1, vmask = -1;
      sse3f v0 = zero, v1 = zero, v2 = zero;

      for (size_t i=0; i<4; i++)
      {
        if (dst.primID[i] == -1) break;
        const unsigned geomID = dst.geomID[i];
        const unsigned primID = dst.primID[i];
        const TriangleMeshScene::TriangleMesh::Triangle& tri = mesh->triangle(primID);
        const Vec3fa p0 = mesh->vertex(tri.v[0]);
        const Vec3fa p1 = mesh->vertex(tri.v[1]);
        const Vec3fa p2 = mesh->vertex(tri.v[2]);
        bounds.extend(merge(BBox3f(p0),BBox3f(p1),BBox3f(p2)));
        vgeomID [i] = geomID;
        vprimID [i] = primID;
        vmask   [i] = mesh->mask;
        v0.x[i] = p0.x; v0.y[i] = p0.y; v0.z[i] = p0.z;
        v1.x[i] = p1.x; v1.y[i] = p1.y; v1.z[i] = p1.z;
        v2.x[i] = p2.x; v2.y[i] = p2.y; v2.z[i] = p2.z;
        }
      new (&dst) Triangle4(v0,v1,v2,vgeomID,vprimID,vmask);
    }
    return bounds; 
  }
Esempio n. 4
0
TEST(BBoxTest, mergedWithBBox) {
    const BBox3f bounds1(Vec3f(-12.0f, -3.0f, 4.0f), Vec3f(7.0f, 8.0f, 9.0f));
    const BBox3f bounds2(Vec3f(-10.0f, -5.0f, 3.0f), Vec3f(9.0f, 9.0f, 5.0f));
    const BBox3f merged( Vec3f(-12.0f, -5.0f, 3.0f), Vec3f(9.0f, 9.0f, 9.0f));
    
    ASSERT_EQ(merged, bounds1.mergedWith(bounds2));
}
  int TriangleMeshWithNormals::extract(RTCScene scene, size_t id) const
  {
    unsigned mesh = rtcNewTriangleMesh (scene, RTC_GEOMETRY_STATIC, triangles.size(), vertices.size());
    //if (mesh != id) throw std::runtime_error("ID does not match");
    Vec3fa* vertices_o = (Vec3fa*) rtcMapBuffer(scene,mesh,RTC_VERTEX_BUFFER); 
    RTCTriangle* triangles_o = (RTCTriangle*) rtcMapBuffer(scene,mesh,RTC_INDEX_BUFFER);
    
    for (size_t j=0; j<triangles.size(); j++) {
      const TriangleMeshWithNormals::Triangle& tri = triangles[j];
      triangles_o[j].v0 = tri.v0;
      triangles_o[j].v1 = tri.v1;
      triangles_o[j].v2 = tri.v2;
    }

    BBox3f bounds = empty;
    for (size_t j=0; j<vertices.size(); j++) {
      const Vector3f p = vertices[j].p;
      vertices_o[j].x = p.x;
      vertices_o[j].y = p.y;
      vertices_o[j].z = p.z;
      bounds.grow(p);
    }
    rtcUnmapBuffer(scene,mesh,RTC_VERTEX_BUFFER); 
    rtcUnmapBuffer(scene,mesh,RTC_INDEX_BUFFER);
    return mesh;
  }
Esempio n. 6
0
TEST(BBoxTest, relativePosition) {
    const BBox3f bounds(Vec3f(-12.0f, -3.0f,  4.0f), Vec3f( 8.0f, 9.0f, 8.0f));
    const Vec3f point1(-1.0f, 0.0f, 0.0f);
    const BBox3f::RelativePosition pos1 = bounds.relativePosition(point1);
    ASSERT_EQ(BBox3f::RelativePosition::Range_Within, pos1[0]);
    ASSERT_EQ(BBox3f::RelativePosition::Range_Within, pos1[1]);
    ASSERT_EQ(BBox3f::RelativePosition::Range_Less,   pos1[2]);
}
Esempio n. 7
0
TEST(BBoxTest, containsBBox) {
    const BBox3f bounds1(Vec3f(-12.0f, -3.0f,  4.0f), Vec3f( 8.0f, 9.0f, 8.0f));
    const BBox3f bounds2(Vec3f(-10.0f, -2.0f,  5.0f), Vec3f( 7.0f, 8.0f, 7.0f));
    const BBox3f bounds3(Vec3f(-13.0f, -2.0f,  5.0f), Vec3f( 7.0f, 8.0f, 7.0f));
    ASSERT_TRUE(bounds1.contains(bounds1));
    ASSERT_TRUE(bounds1.contains(bounds2));
    ASSERT_FALSE(bounds1.contains(bounds3));
}
Esempio n. 8
0
TEST(BBoxTest, intersectsBBox) {
    const BBox3f bounds1(Vec3f(-12.0f, -3.0f,  4.0f), Vec3f(  8.0f,  9.0f,  8.0f));
    const BBox3f bounds2(Vec3f(-10.0f, -2.0f,  5.0f), Vec3f(  7.0f,  8.0f,  7.0f));
    const BBox3f bounds3(Vec3f(-13.0f, -2.0f,  5.0f), Vec3f(  7.0f,  8.0f,  7.0f));
    const BBox3f bounds4(Vec3f(-15.0f, 10.0f,  9.0f), Vec3f(-13.0f, 12.0f, 10.0f));
    const BBox3f bounds5(Vec3f(-15.0f, 10.0f,  9.0f), Vec3f(-12.0f, 12.0f, 10.0f));
    ASSERT_TRUE(bounds1.intersects(bounds1));
    ASSERT_TRUE(bounds1.intersects(bounds2));
    ASSERT_TRUE(bounds1.intersects(bounds3));
    ASSERT_FALSE(bounds1.intersects(bounds4));
    ASSERT_FALSE(bounds1.intersects(bounds5));
}
 BBox3f TriangleMeshWithNormals::extract(size_t id, RTCTriangle* triangles_o, size_t& numTriangles, Vec3fa* positions_o, size_t& numVertices) const
 {
   BBox3f bounds = empty;
   for (size_t j=0; j<triangles.size(); j++) {
     const TriangleMeshWithNormals::Triangle& tri = triangles[j];
     triangles_o[numTriangles++] = RTCTriangle((int)numVertices+tri.v0,(int)numVertices+tri.v1,(int)numVertices+tri.v2,(int)id,(int)j);
   }
   for (size_t j=0; j<vertices.size(); j++) {
     const Vector3f p = vertices[j].p;
     positions_o[numVertices++] = Vector3f(p.x,p.y,p.z);
     bounds.grow(p);
   }
   return bounds;
 }
Esempio n. 10
0
 void BVH4BuilderTopLevel::task_build_parallel(size_t threadIndex, size_t threadCount, 
                                               size_t taskIndex, size_t taskCount,
                                               TaskScheduler::Event* event_i)
 {
   /* ignore meshes that need all threads */
   size_t objectID = taskIndex;
   if (builders[objectID] && builders[objectID]->needAllThreads) 
     return;
   
   /* build all other meshes */
   BBox3f bounds = build(threadIndex,threadCount,objectID);
   if (!bounds.empty()) 
     g_state->thread_bounds[threadIndex].extend(bounds);
 }
Esempio n. 11
0
  void FallBackSplitter<Heuristic,PrimRefBlockList>::split(size_t threadIndex, PrimRefAlloc* alloc, 
                                                           const RTCGeometry* geom,
                                                           PrimRefBlockList& prims, const PrimInfo& pinfo,
                                                           PrimRefBlockList& lprims, PrimInfo& linfo, Split& lsplit,
                                                           PrimRefBlockList& rprims, PrimInfo& rinfo, Split& rsplit)
  {
    /* enforce split */
    size_t lnum = 0; BBox3f lgeomBounds = empty; BBox3f lcentBounds = empty;
    size_t rnum = 0; BBox3f rgeomBounds = empty; BBox3f rcentBounds = empty;
    atomic_set<PrimRefBlock>::item* lblock = lprims.insert(alloc->malloc(threadIndex));
    atomic_set<PrimRefBlock>::item* rblock = rprims.insert(alloc->malloc(threadIndex));
    
    while (atomic_set<PrimRefBlock>::item* block = prims.take()) 
    {
      for (size_t i=0; i<block->size(); i++) 
      {
        const PrimRef& prim = block->at(i); 
        const BBox3f bounds = prim.bounds();
        
        if ((lnum+rnum)&1) 
        {
          lnum++;
          lgeomBounds.grow(bounds);
          lcentBounds.grow(center2(bounds));
          if (likely(lblock->insert(prim))) continue; 
          lblock = lprims.insert(alloc->malloc(threadIndex));
          lblock->insert(prim);
        } else {
          rnum++;
          rgeomBounds.grow(bounds);
          rcentBounds.grow(center2(bounds));
          if (likely(rblock->insert(prim))) continue;
          rblock = rprims.insert(alloc->malloc(threadIndex));
          rblock->insert(prim);
        }
      }
    }
    new (&linfo) PrimInfo(lnum,lgeomBounds,lcentBounds);
    new (&rinfo) PrimInfo(rnum,rgeomBounds,rcentBounds);

    /* perform binning of left side */
    Heuristic lheuristic(linfo,geom);
    typename PrimRefBlockList::iterator liter(lprims);
    while (typename PrimRefBlockList::item* block = liter.next()) {
      lheuristic.bin(block->base(),block->size());
    }
    lheuristic.best(lsplit);

    /* perform binning of right side */
    Heuristic rheuristic(rinfo,geom);
    typename PrimRefBlockList::iterator riter(rprims);
    while (typename PrimRefBlockList::item* block = riter.next()) {
      rheuristic.bin(block->base(),block->size());
    }
    rheuristic.best(rsplit);
  }
Esempio n. 12
0
    void recursiveBuild(uint32_t nodeIndex, uint32_t start, uint32_t end, uint32_t* indices,
                        PositionFunctor getPosition, uint32_t& nextFreeNode) {
        if(start + 1 == end) {
            // One node to process, it's a  leaf
            m_Nodes[nodeIndex].setAsLeaf();

            m_NodesData[nodeIndex].m_nIndex = indices[start];
            m_NodesData[nodeIndex].m_Position = getPosition(indices[start]);

            return;
        }

        // Compute the bounding box of the data
        BBox3f bound;
        for(uint32_t i = start; i != end; ++i) {
            bound += getPosition(indices[i]);
        }
        // The split axis is the one with maximal extent for the data
        uint32_t splitAxis = maxComponent(abs(bound.size()));
        uint32_t splitIndex = (start + end) / 2;
        // Reorganize the pointers such that the middle element is the middle element on the split axis
        std::nth_element(indices + start, indices + splitIndex, indices + end,
                         [splitAxis, &getPosition](uint32_t lhs, uint32_t rhs) -> bool {
                            float v1 = getPosition(lhs)[splitAxis];
                            float v2 = getPosition(rhs)[splitAxis];
                            return v1 == v2 ? lhs < rhs : v1 < v2;
                         });
        float splitPosition = getPosition(indices[splitIndex])[splitAxis];
        m_Nodes[nodeIndex].setAsInnerNode(splitPosition, splitAxis);
        m_NodesData[nodeIndex].m_nIndex = indices[splitIndex];
        m_NodesData[nodeIndex].m_Position = getPosition(indices[splitIndex]);

        // Build the left subtree
        if(start < splitIndex) {
            m_Nodes[nodeIndex].m_bHasLeftChild = true;
            uint32_t childIndex = nextFreeNode++;
            recursiveBuild(childIndex, start, splitIndex, indices, getPosition, nextFreeNode);
        }

        // Build the right subtree
        if(splitIndex + 1 < end) {
            m_Nodes[nodeIndex].m_nRightChildIndex = nextFreeNode++;
            recursiveBuild(m_Nodes[nodeIndex].m_nRightChildIndex, splitIndex + 1, end, indices, getPosition, nextFreeNode);
        }
    }
Esempio n. 13
0
 BBox3f TriangleMeshTriangle1v::update(char* prim, size_t num, void* geom) const 
 {
   BBox3f bounds = empty;
   const TriangleMeshScene::TriangleMesh* mesh = (const TriangleMeshScene::TriangleMesh*) geom;
   
   for (size_t j=0; j<num; j++) 
   {
     Triangle1v& dst = ((Triangle1v*) prim)[j];
     const unsigned geomID = dst.geomID();
     const unsigned primID = dst.primID();
     const TriangleMeshScene::TriangleMesh::Triangle& tri = mesh->triangle(primID);
     const Vec3fa v0 = mesh->vertex(tri.v[0]);
     const Vec3fa v1 = mesh->vertex(tri.v[1]);
     const Vec3fa v2 = mesh->vertex(tri.v[2]);
     new (&dst) Triangle1v(v0,v1,v2,geomID,primID,mesh->mask);
     bounds.extend(merge(BBox3f(v0),BBox3f(v1),BBox3f(v2)));
   }
   return bounds; 
 }
Esempio n. 14
0
TEST(BBoxTest, intersectWithRay) {
    const BBox3f bounds(Vec3f(-12.0f, -3.0f,  4.0f), Vec3f(  8.0f,  9.0f,  8.0f));
    Vec3f normal;
    
    float distance = bounds.intersectWithRay(Ray3f(Vec3f::Null, Vec3f::NegZ));
    ASSERT_TRUE(Math::isnan(distance));
    
    distance = bounds.intersectWithRay(Ray3f(Vec3f::Null, Vec3f::PosZ), &normal);
    ASSERT_FALSE(Math::isnan(distance));
    ASSERT_FLOAT_EQ(4.0f, distance);
    ASSERT_VEC_EQ(Vec3f::NegZ, normal);
    
    const Vec3f origin = Vec3f(-10.0f, -7.0f, 14.0f);
    const Vec3f diff = Vec3f(-2.0f, 3.0f, 8.0f) - origin;
    const Vec3f dir = diff.normalized();
    distance = bounds.intersectWithRay(Ray3f(origin, dir), &normal);
    ASSERT_FALSE(Math::isnan(distance));
    ASSERT_FLOAT_EQ(diff.length(), distance);
    ASSERT_VEC_EQ(Vec3f::PosZ, normal);
    
}
Esempio n. 15
0
void PtexViewer::frameView(bool reset)
{
    if (reset) _cam.reset();
    
    if (_displayFace>=0)
    {
        _cam.setLookAt(Vec3f(0.5, 0.5, 0));
        _cam.setDistance(1.41);
    }
    else
    {
        if (_mode3d && _envCube && reset)
        {
            _cam.setLookAt(Vec3f(0,0,0));
            _cam.setDistance(1e-4);
        }
        else
        {
            _cam.setLookAt(_bounds.getCenter());
            _cam.setDistance(_bounds.diagonal());
        }
    }
}
Esempio n. 16
0
  float BVH4i::sah (NodeRef& node, const BBox3f& bounds)
  {
    float f = bounds.empty() ? 0.0f : area(bounds);

    if (node.isNode()) 
    {
      Node* n = node.node(nodePtr());
      for (size_t c=0; c<4; c++) 
        f += sah(n->child(c),n->bounds(c));
      return f;
    }
    else 
    {
      size_t num; node.leaf(triPtr(),num);
      return f*num;
    }
  }
Esempio n. 17
0
 void FallBackSplitter<Heuristic,PrimRefBlockList>::split(size_t threadIndex, PrimRefAlloc* alloc, 
                                                          const RTCGeometry* geom,
                                                          PrimRefBlockList& prims, const PrimInfo& pinfo,
                                                          PrimRefBlockList& lprims, PrimInfo& linfo,
                                                          PrimRefBlockList& rprims, PrimInfo& rinfo)
 {
   /* enforce split */
   size_t lnum = 0; BBox3f lgeomBounds = empty; BBox3f lcentBounds = empty;
   size_t rnum = 0; BBox3f rgeomBounds = empty; BBox3f rcentBounds = empty;
   atomic_set<PrimRefBlock>::item* lblock = lprims.insert(alloc->malloc(threadIndex));
   atomic_set<PrimRefBlock>::item* rblock = rprims.insert(alloc->malloc(threadIndex));
   
   while (atomic_set<PrimRefBlock>::item* block = prims.take()) 
   {
     for (size_t i=0; i<block->size(); i++) 
     {
       const PrimRef& prim = block->at(i); 
       const BBox3f bounds = prim.bounds();
       
       if ((lnum+rnum)&1) 
       {
         lnum++;
         lgeomBounds.grow(bounds);
         lcentBounds.grow(center2(bounds));
         if (likely(lblock->insert(prim))) continue; 
         lblock = lprims.insert(alloc->malloc(threadIndex));
         lblock->insert(prim);
       } else {
         rnum++;
         rgeomBounds.grow(bounds);
         rcentBounds.grow(center2(bounds));
         if (likely(rblock->insert(prim))) continue;
         rblock = rprims.insert(alloc->malloc(threadIndex));
         rblock->insert(prim);
       }
     }
   }
   new (&linfo) PrimInfo(lnum,lgeomBounds,lcentBounds);
   new (&rinfo) PrimInfo(rnum,rgeomBounds,rcentBounds);
 }
Esempio n. 18
0
TEST(BBoxTest, repaired) {
    const BBox3f bounds  (Vec3f( 3.0f, 4.0f, 0.0f), Vec3f(-1.0f, 0.0f, 1.0f));
    const BBox3f repaired(Vec3f(-1.0f, 0.0f, 0.0f), Vec3f( 3.0f, 4.0f, 1.0f));
    ASSERT_EQ(repaired, bounds.repaired());
}
Esempio n. 19
0
TEST(BBoxTest, translated) {
    const BBox3f bounds    (Vec3f(-12.0f, -3.0f,  4.0f), Vec3f( 8.0f, 9.0f, 8.0f));
    const BBox3f translated(Vec3f(-10.0f, -4.0f,  1.0f), Vec3f(10.0f, 8.0f, 5.0f));
    ASSERT_EQ(translated, bounds.translated(Vec3f(2.0f, -1.0f, -3.0f)));
}
Esempio n. 20
0
TEST(BBoxTest, expanded) {
    const BBox3f bounds  (Vec3f(-12.0f, -3.0f,  4.0f), Vec3f( 8.0f,  9.0f,  8.0f));
    const BBox3f expanded(Vec3f(-14.0f, -5.0f,  2.0f), Vec3f(10.0f, 11.0f, 10.0f));
    ASSERT_EQ(expanded, bounds.expanded(2.0f));
}
void GLVoxelizerTripiana2009::initGLState(uint32_t resolution, BBox3f bbox,
                                          GLVoxelFramebuffer& framebuffer, Mat4f& gridToWorldMatrix) {
    auto v = bbox.upper - bbox.lower;

    auto bboxCenter = 0.5f * (bbox.lower + bbox.upper);
    // Grow of 5% the size of the bounding box to ensure that we don't miss any triangle that are at the edge
    bbox.lower = bboxCenter - 0.55f * v;
    bbox.upper = bboxCenter + 0.55f * v;

    auto m_res = resolution;
    // Requiered number of color buffer
    auto m_numRenderTargets = ceil((double)m_res / 128.0);

    auto bboxSize = bbox.size();
    auto m_AABCLength = reduceMax(bboxSize);

    auto m_voxelLength = m_AABCLength / (float)m_res;


    auto m_origBBox = bboxCenter - glm::vec3(0.5f * m_AABCLength);

    gridToWorldMatrix = scale(translate(Mat4f(1.f), m_origBBox), glm::vec3(m_voxelLength));

    // Use the shaders
    m_Program.use();

    // Desactivate depth, cullface
    glDisable(GL_DEPTH_TEST);
    glDisable(GL_CULL_FACE);

    // Blending activated
    glEnable(GL_COLOR_LOGIC_OP);
    glLogicOp(GL_OR);

    // Init FrameBuffer
    if (!framebuffer.init(m_res, m_res, m_res, m_numRenderTargets)){
        std::cerr << "FBO Error" << std::endl;
    }

    //Projection matrix : Adapt the viewport to the size of the mesh
    glm::mat4 P = glm::ortho(-m_AABCLength * 0.5f,
        m_AABCLength * 0.5f,
        -m_AABCLength * 0.5f,
        m_AABCLength * 0.5f,
        0.f,
        m_AABCLength);

    glm::vec3 position(bboxCenter.x, bboxCenter.y, bboxCenter.z + 0.5 * m_AABCLength);
    glm::vec3 point(bboxCenter.x, bboxCenter.y, bboxCenter.z);
    glm::mat4 V = glm::lookAt(position, point, glm::vec3(0, 1, 0));

    // Get the MVP Matrix
    glm::mat4 MVPMatrix = P * V;

    // Set uniforms
    MVP.set(MVPMatrix);
    halfPixelSize.set(Vec2f(1.f / m_res));
    numVoxels.set(resolution);
    origBBox.set(m_origBBox);
    numRenderTargets.set(m_numRenderTargets);
    voxelSize.set(m_voxelLength);

    framebuffer.bind(GL_DRAW_FRAMEBUFFER);

    // Set the list of draw buffers.
    std::vector<GLenum> DrawBuffers(m_numRenderTargets, 0);
    for (int i = 0; i < m_numRenderTargets; ++i){
        DrawBuffers[i] = GL_COLOR_ATTACHMENT0 + i;
    }
    glDrawBuffers(m_numRenderTargets, DrawBuffers.data());

    glViewport(0, 0, m_res, m_res);

    // Clear the window
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
Esempio n. 22
0
TEST(BBoxTest, containsPoint) {
    const BBox3f bounds(Vec3f(-12.0f, -3.0f,  4.0f), Vec3f( 8.0f, 9.0f, 8.0f));
    ASSERT_TRUE(bounds.contains(Vec3f(2.0f, 1.0f, 7.0f)));
    ASSERT_TRUE(bounds.contains(Vec3f(-12.0f, -3.0f, 7.0f)));
    ASSERT_FALSE(bounds.contains(Vec3f(-13.0f, -3.0f, 7.0f)));
}
Esempio n. 23
0
TEST(BBoxTest, translatedToOrigin) {
    const BBox3f bounds    (Vec3f(-12.0f, -3.0f,  4.0f), Vec3f( 8.0f, 9.0f, 8.0f));
    const BBox3f translated(Vec3f(-10.0f, -6.0f, -2.0f), Vec3f(10.0f, 6.0f, 2.0f));
    ASSERT_EQ(translated, bounds.translatedToOrigin());
}