9

How can I write a script to create a NURBS surface with given control points?

I did find a helpful piece of source code that got me here:

import bpy

surfacedata = bpy.data.curves.new(name='test', type='SURFACE')
surfacedata.dimensions = '3D'
surfacedata.resolution_u = 4
surfacedata.resolution_v = 4

objectdata = bpy.data.objects.new('obj_test', surfacedata)
objectdata.location = (0,0,0)
bpy.context.scene.objects.link(objectdata)

nurbs_surface = surfacedata.splines.new('NURBS')
nurbs_surface.points.add(4)
nurbs_surface.points[0].co = (-1, -1,  0.25, 1)
nurbs_surface.points[1].co = (-1,  1, -0.25, 1)
nurbs_surface.points[2].co = ( 1, -1, -0.25, 1)
nurbs_surface.points[3].co = ( 1,  1,  0.25, 1)
nurbs_surface.order_u = 2
nurbs_surface.order_v = 2
nurbs_surface.use_endpoint_u = True
nurbs_surface.use_endpoint_v = True

However, the object that this produces is just a 1D curve in space,

test

I can check some of its properties from the python console,

>>> print(len(bpy.data.objects[2].data.splines[0].points))
5

>>> print(bpy.data.objects[2].data.splines[0].point_count_u)
5

>>> print(bpy.data.objects[2].data.splines[0].point_count_v)
1

So, I'm not sure why points has 5 elements (instead of 4), and why point_count_u and point_count_v are 5 and 1 instead of 2 and 2, respectively.

David
  • 49,291
  • 38
  • 159
  • 317
mack
  • 113
  • 1
  • 4
  • NURBS surfaces are controlled by a mesh of squares. If you add a new NURBS surface and go into Edit Mode, you'll see a 4x4 mesh of control points. In your case, you've created a 5x1 "control point mesh" (it had 1 control point by default and you added 4). Creating a mesh for an object is easy, but it might be the case that bpy doesn't (yet) give you access to this control point mesh, in which case, I can't see how you'd do this. – Garrett Feb 23 '14 at 05:17
  • We seem to lack the ability to set the variables point_count_v and point_count_u too – zeffii Apr 07 '14 at 16:24
  • @ideasman42, maybe could chime in? – zeffii Apr 07 '14 at 20:33

1 Answers1

12

While it's not very pretty, it is possible. This example recreates the default NURBS Surface. Inspired by https://blender.stackexchange.com/a/8587/47

import bpy
from mathutils import Vector

surface_data = bpy.data.curves.new('wook', 'SURFACE') surface_data.dimensions = '3D'

16 coordinates

points = [ Vector((-1.5, -1.5, 0.0, 1.0)), Vector((-1.5, -0.5, 0.0, 1.0)), Vector((-1.5, 0.5, 0.0, 1.0)), Vector((-1.5, 1.5, 0.0, 1.0)), Vector((-0.5, -1.5, 0.0, 1.0)), Vector((-0.5, -0.5, 1.0, 1.0)), Vector((-0.5, 0.5, 1.0, 1.0)), Vector((-0.5, 1.5, 0.0, 1.0)), Vector((0.5, -1.5, 0.0, 1.0)), Vector((0.5, -0.5, 1.0, 1.0)), Vector((0.5, 0.5, 1.0, 1.0)), Vector((0.5, 1.5, 0.0, 1.0)), Vector((1.5, -1.5, 0.0, 1.0)), Vector((1.5, -0.5, 0.0, 1.0)), Vector((1.5, 0.5, 0.0, 1.0)), Vector((1.5, 1.5, 0.0, 1.0)) ]

set points per segments (U * V)

for i in range(0, 16, 4): spline = surface_data.splines.new(type='NURBS') spline.points.add(3) # already has a default vector

for p, new_co in zip(spline.points, points[i:i+4]):
    p.co = new_co

surface_object = bpy.data.objects.new('NURBS_OBJ', surface_data) bpy.context.collection.objects.link(surface_object)

splines = surface_object.data.splines for s in splines: for p in s.points: p.select = True

bpy.context.view_layer.objects.active = surface_object bpy.ops.object.mode_set(mode = 'EDIT') bpy.ops.curve.make_segment()

The ideal situation would be where the spline.points would take a nested list and automatically combine them under the hood, rather than requiring calls to bpy.ops.

Edit: For Blender 2.8+, linking objects uses 'bpy.context.collection'

feesta
  • 3
  • 2
zeffii
  • 39,634
  • 9
  • 103
  • 186
  • Thanks, I should be able to get what I want out of your reply. There might also be a method using https://docs.blender.org/api/244PythonDoc/Curve.SurfNurb-class.html But yours looks simpler to follow for an amateur – James Hamilton Feb 10 '17 at 09:16
  • that's Blender 244 documentation - none of that will work directly in 2.7+ – zeffii Feb 11 '17 at 10:37