Example #1
0
float Camera::world_to_raster_size(float3 P)
{
	if(type == CAMERA_ORTHOGRAPHIC) {
		return min(len(full_dx), len(full_dy));
	}
	else if(type == CAMERA_PERSPECTIVE) {
		/* Calculate as if point is directly ahead of the camera. */
		float3 raster = make_float3(0.5f*width, 0.5f*height, 0.0f);
		float3 Pcamera = transform_perspective(&rastertocamera, raster);

		/* dDdx */
		float3 Ddiff = transform_direction(&cameratoworld, Pcamera);
		float3 dx = len_squared(full_dx) < len_squared(full_dy) ? full_dx : full_dy;
		float3 dDdx = normalize(Ddiff + dx) - normalize(Ddiff);

		/* dPdx */
		float dist = len(transform_point(&worldtocamera, P));
		float3 D = normalize(Ddiff);
		return len(dist*dDdx - dot(dist*dDdx, D)*D);
	}
	else {
		// TODO(mai): implement for CAMERA_PANORAMA
		assert(!"pixel width calculation for panoramic projection not implemented yet");
	}

	return 1.0f;
}
Example #2
0
bool BlenderObjectCulling::test_distance(Scene *scene, float3 bb[8])
{
	float3 camera_position = transform_get_column(&scene->camera->matrix, 3);
	float3 bb_min = make_float3(FLT_MAX, FLT_MAX, FLT_MAX),
	       bb_max = make_float3(-FLT_MAX, -FLT_MAX, -FLT_MAX);

	/* Find min & max points for x & y & z on bounding box */
	for(int i = 0; i < 8; ++i) {
		float3 p = bb[i];
		bb_min = min(bb_min, p);
		bb_max = max(bb_max, p);
	}

	float3 closest_point = max(min(bb_max,camera_position),bb_min);
	return (len_squared(camera_position - closest_point) >
	        distance_cull_margin_ * distance_cull_margin_);
}
Example #3
0
static void attr_create_pointiness(Scene *scene, Mesh *mesh, BL::Mesh &b_mesh, bool subdivision)
{
  if (!mesh->need_attribute(scene, ATTR_STD_POINTINESS)) {
    return;
  }
  const int num_verts = b_mesh.vertices.length();
  if (num_verts == 0) {
    return;
  }
  /* STEP 1: Find out duplicated vertices and point duplicates to a single
   *         original vertex.
   */
  vector<int> sorted_vert_indeices(num_verts);
  for (int vert_index = 0; vert_index < num_verts; ++vert_index) {
    sorted_vert_indeices[vert_index] = vert_index;
  }
  VertexAverageComparator compare(mesh->verts);
  sort(sorted_vert_indeices.begin(), sorted_vert_indeices.end(), compare);
  /* This array stores index of the original vertex for the given vertex
   * index.
   */
  vector<int> vert_orig_index(num_verts);
  for (int sorted_vert_index = 0; sorted_vert_index < num_verts; ++sorted_vert_index) {
    const int vert_index = sorted_vert_indeices[sorted_vert_index];
    const float3 &vert_co = mesh->verts[vert_index];
    bool found = false;
    for (int other_sorted_vert_index = sorted_vert_index + 1; other_sorted_vert_index < num_verts;
         ++other_sorted_vert_index) {
      const int other_vert_index = sorted_vert_indeices[other_sorted_vert_index];
      const float3 &other_vert_co = mesh->verts[other_vert_index];
      /* We are too far away now, we wouldn't have duplicate. */
      if ((other_vert_co.x + other_vert_co.y + other_vert_co.z) -
              (vert_co.x + vert_co.y + vert_co.z) >
          3 * FLT_EPSILON) {
        break;
      }
      /* Found duplicate. */
      if (len_squared(other_vert_co - vert_co) < FLT_EPSILON) {
        found = true;
        vert_orig_index[vert_index] = other_vert_index;
        break;
      }
    }
    if (!found) {
      vert_orig_index[vert_index] = vert_index;
    }
  }
  /* Make sure we always points to the very first orig vertex. */
  for (int vert_index = 0; vert_index < num_verts; ++vert_index) {
    int orig_index = vert_orig_index[vert_index];
    while (orig_index != vert_orig_index[orig_index]) {
      orig_index = vert_orig_index[orig_index];
    }
    vert_orig_index[vert_index] = orig_index;
  }
  sorted_vert_indeices.free_memory();
  /* STEP 2: Calculate vertex normals taking into account their possible
   *         duplicates which gets "welded" together.
   */
  vector<float3> vert_normal(num_verts, make_float3(0.0f, 0.0f, 0.0f));
  /* First we accumulate all vertex normals in the original index. */
  for (int vert_index = 0; vert_index < num_verts; ++vert_index) {
    const float3 normal = get_float3(b_mesh.vertices[vert_index].normal());
    const int orig_index = vert_orig_index[vert_index];
    vert_normal[orig_index] += normal;
  }
  /* Then we normalize the accumulated result and flush it to all duplicates
   * as well.
   */
  for (int vert_index = 0; vert_index < num_verts; ++vert_index) {
    const int orig_index = vert_orig_index[vert_index];
    vert_normal[vert_index] = normalize(vert_normal[orig_index]);
  }
  /* STEP 3: Calculate pointiness using single ring neighborhood. */
  vector<int> counter(num_verts, 0);
  vector<float> raw_data(num_verts, 0.0f);
  vector<float3> edge_accum(num_verts, make_float3(0.0f, 0.0f, 0.0f));
  BL::Mesh::edges_iterator e;
  EdgeMap visited_edges;
  int edge_index = 0;
  memset(&counter[0], 0, sizeof(int) * counter.size());
  for (b_mesh.edges.begin(e); e != b_mesh.edges.end(); ++e, ++edge_index) {
    const int v0 = vert_orig_index[b_mesh.edges[edge_index].vertices()[0]],
              v1 = vert_orig_index[b_mesh.edges[edge_index].vertices()[1]];
    if (visited_edges.exists(v0, v1)) {
      continue;
    }
    visited_edges.insert(v0, v1);
    float3 co0 = get_float3(b_mesh.vertices[v0].co()), co1 = get_float3(b_mesh.vertices[v1].co());
    float3 edge = normalize(co1 - co0);
    edge_accum[v0] += edge;
    edge_accum[v1] += -edge;
    ++counter[v0];
    ++counter[v1];
  }
  for (int vert_index = 0; vert_index < num_verts; ++vert_index) {
    const int orig_index = vert_orig_index[vert_index];
    if (orig_index != vert_index) {
      /* Skip duplicates, they'll be overwritten later on. */
      continue;
    }
    if (counter[vert_index] > 0) {
      const float3 normal = vert_normal[vert_index];
      const float angle = safe_acosf(dot(normal, edge_accum[vert_index] / counter[vert_index]));
      raw_data[vert_index] = angle * M_1_PI_F;
    }
    else {
      raw_data[vert_index] = 0.0f;
    }
  }
  /* STEP 3: Blur vertices to approximate 2 ring neighborhood. */
  AttributeSet &attributes = (subdivision) ? mesh->subd_attributes : mesh->attributes;
  Attribute *attr = attributes.add(ATTR_STD_POINTINESS);
  float *data = attr->data_float();
  memcpy(data, &raw_data[0], sizeof(float) * raw_data.size());
  memset(&counter[0], 0, sizeof(int) * counter.size());
  edge_index = 0;
  visited_edges.clear();
  for (b_mesh.edges.begin(e); e != b_mesh.edges.end(); ++e, ++edge_index) {
    const int v0 = vert_orig_index[b_mesh.edges[edge_index].vertices()[0]],
              v1 = vert_orig_index[b_mesh.edges[edge_index].vertices()[1]];
    if (visited_edges.exists(v0, v1)) {
      continue;
    }
    visited_edges.insert(v0, v1);
    data[v0] += raw_data[v1];
    data[v1] += raw_data[v0];
    ++counter[v0];
    ++counter[v1];
  }
  for (int vert_index = 0; vert_index < num_verts; ++vert_index) {
    data[vert_index] /= counter[vert_index] + 1;
  }
  /* STEP 4: Copy attribute to the duplicated vertices. */
  for (int vert_index = 0; vert_index < num_verts; ++vert_index) {
    const int orig_index = vert_orig_index[vert_index];
    data[vert_index] = data[orig_index];
  }
}
Example #4
0
static void ExportCurveSegmentsMotion(Mesh *mesh, ParticleCurveData *CData, int motion_step)
{
  VLOG(1) << "Exporting curve motion segments for mesh " << mesh->name << ", motion step "
          << motion_step;

  /* find attribute */
  Attribute *attr_mP = mesh->curve_attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
  bool new_attribute = false;

  /* add new attribute if it doesn't exist already */
  if (!attr_mP) {
    VLOG(1) << "Creating new motion vertex position attribute";
    attr_mP = mesh->curve_attributes.add(ATTR_STD_MOTION_VERTEX_POSITION);
    new_attribute = true;
  }

  /* export motion vectors for curve keys */
  size_t numkeys = mesh->curve_keys.size();
  float4 *mP = attr_mP->data_float4() + motion_step * numkeys;
  bool have_motion = false;
  int i = 0;
  int num_curves = 0;

  for (int sys = 0; sys < CData->psys_firstcurve.size(); sys++) {
    for (int curve = CData->psys_firstcurve[sys];
         curve < CData->psys_firstcurve[sys] + CData->psys_curvenum[sys];
         curve++) {
      /* Curve lengths may not match! Curves can be clipped. */
      int curve_key_end = (num_curves + 1 < (int)mesh->curve_first_key.size() ?
                               mesh->curve_first_key[num_curves + 1] :
                               (int)mesh->curve_keys.size());
      const int num_center_curve_keys = curve_key_end - mesh->curve_first_key[num_curves];
      const int is_num_keys_different = CData->curve_keynum[curve] - num_center_curve_keys;

      if (!is_num_keys_different) {
        for (int curvekey = CData->curve_firstkey[curve];
             curvekey < CData->curve_firstkey[curve] + CData->curve_keynum[curve];
             curvekey++) {
          if (i < mesh->curve_keys.size()) {
            mP[i] = CurveSegmentMotionCV(CData, sys, curve, curvekey);
            if (!have_motion) {
              /* unlike mesh coordinates, these tend to be slightly different
               * between frames due to particle transforms into/out of object
               * space, so we use an epsilon to detect actual changes */
              float4 curve_key = float3_to_float4(mesh->curve_keys[i]);
              curve_key.w = mesh->curve_radius[i];
              if (len_squared(mP[i] - curve_key) > 1e-5f * 1e-5f)
                have_motion = true;
            }
          }
          i++;
        }
      }
      else {
        /* Number of keys has changed. Generate an interpolated version
         * to preserve motion blur. */
        const float step_size = num_center_curve_keys > 1 ? 1.0f / (num_center_curve_keys - 1) :
                                                            0.0f;
        for (int step_index = 0; step_index < num_center_curve_keys; ++step_index) {
          const float step = step_index * step_size;
          mP[i] = LerpCurveSegmentMotionCV(CData, sys, curve, step);
          i++;
        }
        have_motion = true;
      }
      num_curves++;
    }
  }

  /* in case of new attribute, we verify if there really was any motion */
  if (new_attribute) {
    if (i != numkeys || !have_motion) {
      /* No motion or hair "topology" changed, remove attributes again. */
      if (i != numkeys) {
        VLOG(1) << "Hair topology changed, removing attribute.";
      }
      else {
        VLOG(1) << "No motion, removing attribute.";
      }
      mesh->curve_attributes.remove(ATTR_STD_MOTION_VERTEX_POSITION);
    }
    else if (motion_step > 0) {
      VLOG(1) << "Filling in new motion vertex position for motion_step " << motion_step;
      /* motion, fill up previous steps that we might have skipped because
       * they had no motion, but we need them anyway now */
      for (int step = 0; step < motion_step; step++) {
        float4 *mP = attr_mP->data_float4() + step * numkeys;

        for (int key = 0; key < numkeys; key++) {
          mP[key] = float3_to_float4(mesh->curve_keys[key]);
          mP[key].w = mesh->curve_radius[key];
        }
      }
    }
  }
}
Example #5
0
static void ExportCurveTriangleGeometry(Mesh *mesh, ParticleCurveData *CData, int resolution)
{
  int vertexno = mesh->verts.size();
  int vertexindex = vertexno;
  int numverts = 0, numtris = 0;

  /* compute and reserve size of arrays */
  for (int sys = 0; sys < CData->psys_firstcurve.size(); sys++) {
    for (int curve = CData->psys_firstcurve[sys];
         curve < CData->psys_firstcurve[sys] + CData->psys_curvenum[sys];
         curve++) {
      numverts += (CData->curve_keynum[curve] - 1) * resolution + resolution;
      numtris += (CData->curve_keynum[curve] - 1) * 2 * resolution;
    }
  }

  mesh->reserve_mesh(mesh->verts.size() + numverts, mesh->num_triangles() + numtris);

  /* actually export */
  for (int sys = 0; sys < CData->psys_firstcurve.size(); sys++) {
    for (int curve = CData->psys_firstcurve[sys];
         curve < CData->psys_firstcurve[sys] + CData->psys_curvenum[sys];
         curve++) {
      float3 firstxbasis = cross(make_float3(1.0f, 0.0f, 0.0f),
                                 CData->curvekey_co[CData->curve_firstkey[curve] + 1] -
                                     CData->curvekey_co[CData->curve_firstkey[curve]]);
      if (!is_zero(firstxbasis))
        firstxbasis = normalize(firstxbasis);
      else
        firstxbasis = normalize(cross(make_float3(0.0f, 1.0f, 0.0f),
                                      CData->curvekey_co[CData->curve_firstkey[curve] + 1] -
                                          CData->curvekey_co[CData->curve_firstkey[curve]]));

      for (int curvekey = CData->curve_firstkey[curve];
           curvekey < CData->curve_firstkey[curve] + CData->curve_keynum[curve] - 1;
           curvekey++) {
        float3 xbasis = firstxbasis;
        float3 v1;
        float3 v2;

        if (curvekey == CData->curve_firstkey[curve]) {
          v1 = CData->curvekey_co[min(
                   curvekey + 2, CData->curve_firstkey[curve] + CData->curve_keynum[curve] - 1)] -
               CData->curvekey_co[curvekey + 1];
          v2 = CData->curvekey_co[curvekey + 1] - CData->curvekey_co[curvekey];
        }
        else if (curvekey == CData->curve_firstkey[curve] + CData->curve_keynum[curve] - 1) {
          v1 = CData->curvekey_co[curvekey] - CData->curvekey_co[curvekey - 1];
          v2 = CData->curvekey_co[curvekey - 1] -
               CData->curvekey_co[max(curvekey - 2, CData->curve_firstkey[curve])];
        }
        else {
          v1 = CData->curvekey_co[curvekey + 1] - CData->curvekey_co[curvekey];
          v2 = CData->curvekey_co[curvekey] - CData->curvekey_co[curvekey - 1];
        }

        xbasis = cross(v1, v2);

        if (len_squared(xbasis) >= 0.05f * len_squared(v1) * len_squared(v2)) {
          firstxbasis = normalize(xbasis);
          break;
        }
      }

      for (int curvekey = CData->curve_firstkey[curve];
           curvekey < CData->curve_firstkey[curve] + CData->curve_keynum[curve] - 1;
           curvekey++) {
        int subv = 1;
        float3 xbasis;
        float3 ybasis;
        float3 v1;
        float3 v2;

        if (curvekey == CData->curve_firstkey[curve]) {
          subv = 0;
          v1 = CData->curvekey_co[min(
                   curvekey + 2, CData->curve_firstkey[curve] + CData->curve_keynum[curve] - 1)] -
               CData->curvekey_co[curvekey + 1];
          v2 = CData->curvekey_co[curvekey + 1] - CData->curvekey_co[curvekey];
        }
        else if (curvekey == CData->curve_firstkey[curve] + CData->curve_keynum[curve] - 1) {
          v1 = CData->curvekey_co[curvekey] - CData->curvekey_co[curvekey - 1];
          v2 = CData->curvekey_co[curvekey - 1] -
               CData->curvekey_co[max(curvekey - 2, CData->curve_firstkey[curve])];
        }
        else {
          v1 = CData->curvekey_co[curvekey + 1] - CData->curvekey_co[curvekey];
          v2 = CData->curvekey_co[curvekey] - CData->curvekey_co[curvekey - 1];
        }

        xbasis = cross(v1, v2);

        if (len_squared(xbasis) >= 0.05f * len_squared(v1) * len_squared(v2)) {
          xbasis = normalize(xbasis);
          firstxbasis = xbasis;
        }
        else
          xbasis = firstxbasis;

        ybasis = normalize(cross(xbasis, v2));

        for (; subv <= 1; subv++) {
          float3 ickey_loc = make_float3(0.0f, 0.0f, 0.0f);
          float time = 0.0f;

          InterpolateKeySegments(subv, 1, curvekey, curve, &ickey_loc, &time, CData);

          float radius = shaperadius(CData->psys_shape[sys],
                                     CData->psys_rootradius[sys],
                                     CData->psys_tipradius[sys],
                                     time);

          if ((curvekey == CData->curve_firstkey[curve] + CData->curve_keynum[curve] - 2) &&
              (subv == 1))
            radius = shaperadius(CData->psys_shape[sys],
                                 CData->psys_rootradius[sys],
                                 CData->psys_tipradius[sys],
                                 0.95f);

          if (CData->psys_closetip[sys] && (subv == 1) &&
              (curvekey == CData->curve_firstkey[curve] + CData->curve_keynum[curve] - 2))
            radius = shaperadius(CData->psys_shape[sys], CData->psys_rootradius[sys], 0.0f, 0.95f);

          float angle = M_2PI_F / (float)resolution;
          for (int section = 0; section < resolution; section++) {
            float3 ickey_loc_shf = ickey_loc + radius * (cosf(angle * section) * xbasis +
                                                         sinf(angle * section) * ybasis);
            mesh->add_vertex(ickey_loc_shf);
          }

          if (subv != 0) {
            for (int section = 0; section < resolution - 1; section++) {
              mesh->add_triangle(vertexindex - resolution + section,
                                 vertexindex + section,
                                 vertexindex - resolution + section + 1,
                                 CData->psys_shader[sys],
                                 true);
              mesh->add_triangle(vertexindex + section + 1,
                                 vertexindex - resolution + section + 1,
                                 vertexindex + section,
                                 CData->psys_shader[sys],
                                 true);
            }
            mesh->add_triangle(vertexindex - 1,
                               vertexindex + resolution - 1,
                               vertexindex - resolution,
                               CData->psys_shader[sys],
                               true);
            mesh->add_triangle(vertexindex,
                               vertexindex - resolution,
                               vertexindex + resolution - 1,
                               CData->psys_shader[sys],
                               true);
          }
          vertexindex += resolution;
        }
      }
    }
  }

  mesh->resize_mesh(mesh->verts.size(), mesh->num_triangles());
  mesh->attributes.remove(ATTR_STD_VERTEX_NORMAL);
  mesh->attributes.remove(ATTR_STD_FACE_NORMAL);
  mesh->add_face_normals();
  mesh->add_vertex_normals();
  mesh->attributes.remove(ATTR_STD_FACE_NORMAL);

  /* texture coords still needed */
}
Example #6
0
void test_07() {
    //// WRITES SYSTEM ENERGY TO FILE

    int width = 600;
    int height = 400;
    Object* A = new Player;
    Object* B = new Object;
    Object* C = new Object;
    Object* D = new Object;

    Window window = Window(width,height,NULL,30,vis::AUTO_SIZE_UNIVERSE);

    // Set them apart, and on a collision course
    A->set_position(2, 0);
    A->set_velocity(0,0);
    A->set_radius(0.5);
    A->set_mass(5);
    A->bouncyness = 1;

    B->set_position(2, 2);
    B->set_velocity(0, -1);
    B->set_mass(2);
    //B->bouncyness = 0.7;

    C->set_position(-2, -2);
    C->set_mass(2);
    //C->bouncyness = 0.7;

    D->set_position(-4, -4);
    D->set_mass(2);
    //D->bouncyness = 0.7;



    // Generate a universe
    Universe universe(width/window.pixRatio, height/window.pixRatio);
    window.bindUniverse(&universe);
    // Add them to the universe
    universe.add_object(A);
    universe.add_object(B);
    universe.add_object(C);
    universe.add_object(D);

    vec2d posA;
    vec2d posB;


    std::ofstream outputfile;
    outputfile.open("test_07.csv");

    Universe* uni = &universe;
    do{

        for(int ii = 0; ii < universe.objects.size(); ii++){
            double vel2 = len_squared(universe.objects[ii]->get_velocity());
            outputfile << 0.5*universe.objects[ii]->get_mass()*vel2 << ",";

            double ener = 0;
            for (int qq = 0; qq < universe.objects.size(); ++qq ) {
                // Make sure you are not calculating yourself
                if (qq == ii) {
                    // This is myself, skip this loop iteration
                    continue;
                }
                // Here add up the contribution to the acceleration
                ener += potentialEnergy(universe.objects[ii], universe.objects[qq],uni);
            }
            //// Made the potential energy per object only store half, as for each potential between 2 objects 2 objects store it but there is only one potential.
            if(ii == universe.objects.size()-1){
                outputfile << ener/2 << ";" << std::endl;
            }else{
                outputfile << ener/2 << ",";

            }
        }
        // Clear the buffers to set values (in our case only colour buffer needs to be cleared)
        glClear(GL_COLOR_BUFFER_BIT);

        // Draw the universe's objects on top of that
        window.drawObjectList(universe.objects);

        // Do a physics iteration
        universe.simulate_one_time_unit(window.fps);

        // Swap buffers
        glfwSwapBuffers(window.GLFWpointer);
        glfwPollEvents();

        window.pace_frame();

    } // Check if the ESC key was pressed or the window was closed
    while( glfwGetKey(window.GLFWpointer, GLFW_KEY_ESCAPE ) != GLFW_PRESS &&
           glfwWindowShouldClose(window.GLFWpointer) == 0 );

    outputfile.close();
    // Close OpenGL window and terminate GLFW
    glfwTerminate();

}
Example #7
0
static void ExportCurveSegmentsMotion(Mesh *mesh, ParticleCurveData *CData, int motion_step)
{
	VLOG(1) << "Exporting curve motion segments for mesh " << mesh->name
	        << ", motion step " << motion_step;

	/* find attribute */
	Attribute *attr_mP = mesh->curve_attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
	bool new_attribute = false;

	/* add new attribute if it doesn't exist already */
	if(!attr_mP) {
		VLOG(1) << "Creating new motion vertex position attribute";
		attr_mP = mesh->curve_attributes.add(ATTR_STD_MOTION_VERTEX_POSITION);
		new_attribute = true;
	}

	/* export motion vectors for curve keys */
	size_t numkeys = mesh->curve_keys.size();
	float4 *mP = attr_mP->data_float4() + motion_step*numkeys;
	bool have_motion = false;
	int i = 0;

	for(int sys = 0; sys < CData->psys_firstcurve.size(); sys++) {
		if(CData->psys_curvenum[sys] == 0)
			continue;

		for(int curve = CData->psys_firstcurve[sys]; curve < CData->psys_firstcurve[sys] + CData->psys_curvenum[sys]; curve++) {
			if(CData->curve_keynum[curve] <= 1 || CData->curve_length[curve] == 0.0f)
				continue;

			for(int curvekey = CData->curve_firstkey[curve]; curvekey < CData->curve_firstkey[curve] + CData->curve_keynum[curve]; curvekey++) {
				if(i < mesh->curve_keys.size()) {
					float3 ickey_loc = CData->curvekey_co[curvekey];
					float time = CData->curvekey_time[curvekey]/CData->curve_length[curve];
					float radius = shaperadius(CData->psys_shape[sys], CData->psys_rootradius[sys], CData->psys_tipradius[sys], time);

					if(CData->psys_closetip[sys] && (curvekey == CData->curve_firstkey[curve] + CData->curve_keynum[curve] - 1))
						radius = 0.0f;

					/* curve motion keys store both position and radius in float4 */
					mP[i] = float3_to_float4(ickey_loc);
					mP[i].w = radius;

					/* unlike mesh coordinates, these tend to be slightly different
					 * between frames due to particle transforms into/out of object
					 * space, so we use an epsilon to detect actual changes */
					float4 curve_key = float3_to_float4(mesh->curve_keys[i]);
					curve_key.w = mesh->curve_radius[i];
					if(len_squared(mP[i] - curve_key) > 1e-5f*1e-5f)
						have_motion = true;
				}

				i++;
			}
		}
	}

	/* in case of new attribute, we verify if there really was any motion */
	if(new_attribute) {
		if(i != numkeys || !have_motion) {
			/* No motion or hair "topology" changed, remove attributes again. */
			if(i != numkeys) {
				VLOG(1) << "Hair topology changed, removing attribute.";
			}
			else {
				VLOG(1) << "No motion, removing attribute.";
			}
			mesh->curve_attributes.remove(ATTR_STD_MOTION_VERTEX_POSITION);
		}
		else if(motion_step > 0) {
			VLOG(1) << "Filling in new motion vertex position for motion_step "
			        << motion_step;
			/* motion, fill up previous steps that we might have skipped because
			 * they had no motion, but we need them anyway now */
			for(int step = 0; step < motion_step; step++) {
				float4 *mP = attr_mP->data_float4() + step*numkeys;

				for(int key = 0; key < numkeys; key++) {
					mP[key] = float3_to_float4(mesh->curve_keys[key]);
					mP[key].w = mesh->curve_radius[key];
				}
			}
		}
	}
}