2
$\begingroup$

I am writing a toy operator which select the visible vertices of a mesh from active camera.

My approach is to do a perspective projection on vertices of that mesh to NDC(Normalized Device Coordinates. It is a glossary from rasterization graphic API like OpenGL) and check that whether the NDC is in range of [-1, 1]^3.

I encounter a problem when I construct the projection matrix using camera.view_frame() function: It seems that view_frame() returns double of the real size of camera's sensor.

I am confused with this behavior of view_frame().


Supplementary background: I write a Blog post for details about this toy operator on http://linearconstraints.net/?p=53

And the full source code is on https://gist.github.com/thebusytypist/8900746

Note that I divide the left/right/bottom/top by 2 in line 75-76.

$\endgroup$
  • $\begingroup$ Comparing Camera.clip_start with the z-values of Camera.view_frame() it seems it is being doubled. There is Camera.clip_end, Camera.fov and you might get the aspect ratio from Scene.render.resolution_x/y. An explanation how exactly blender does the calculation would indeed be helpful. $\endgroup$ – pink vertex Feb 9 '14 at 18:22
3
$\begingroup$

There's a utility function world_to_camera_view(), which makes it really easy to select the view frustum:

import bpy
import bmesh
from bpy_extras.object_utils import world_to_camera_view

def main(self, context):
    sce = context.scene
    cam = sce.camera
    ob = context.object
    me = ob.data
    mat = ob.matrix_world

    if not me.is_editmode:
        bpy.ops.object.mode_set(mode='EDIT')

    bm = bmesh.from_edit_mesh(me)

    if not self.extend:
        bpy.ops.mesh.select_all(action='DESELECT')

    for v in bm.verts:
        x, y, z = world_to_camera_view(sce, cam, mat * v.co)
        if (0.0 <= x <= 1.0 and 
            0.0 <= y <= 1.0 and
            (not self.clip or cam.data.clip_start < z < cam.data.clip_end)):
            v.select = True

    bm.select_flush(True)


class MESH_OT_select_view_frustum(bpy.types.Operator):
    """Select geometry in view frustum (uses active object and scene camera)"""
    bl_idname = "mesh.select_view_frustum"
    bl_label = "Select View Frustum"
    bl_options = {'REGISTER', 'UNDO'}

    clip = bpy.props.BoolProperty(
        name="Clip",
        description="Take camera near and far clipping distances into account",
        default=True
    )

    extend = bpy.props.BoolProperty(
        name="Extend",
        description="Extend the current selection",
        default=False
    )

    @classmethod
    def poll(cls, context):
        return (context.object is not None and
                context.object.type == 'MESH' and
                context.scene.camera is not None)

    def execute(self, context):
        main(self, context)
        return {'FINISHED'}


def register():
    bpy.utils.register_class(MESH_OT_select_view_frustum)


def unregister():
    bpy.utils.unregister_class(MESH_OT_select_view_frustum)


if __name__ == "__main__":
    register()
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.