Similiar to icYou520's answer you could create a special lamp and add a volumetric container. Since this is very render-intense here comes another hack.
Model by jahjavjaz
Is is a planar ngon which move vertices away from the center untill they detect a collision. This is done by a somewhat interactive python handler.
Execute the first code sample.
The beginning holds some constants like the name of the object and the maximum distance of the rays. Increase the Radius step size will result in more vertices, and more calculation time.
import bpy
import bmesh
from mathutils.bvhtree import BVHTree
import mathutils
import math
import numpy as np
EPSILON = 0.00001
MAXIMUM_DISTANCE = 10
object_name = 'light_plane'
radius_start = math.pi*1/4
radius_end = math.pi*3/4
radius_step = math.pi/256
angles = [(math.cos(a), math.sin(a)) for a in np.arange(radius_start, radius_end + 0.00001, radius_step)]
def create_mesh_obj(bm, p_name = "from_bmesh"):
scn = bpy.context.scene
me = bpy.data.meshes.new(name = p_name)
bm.to_mesh(me)
if (p_name not in bpy.data.objects):
ob = bpy.data.objects.new(name = p_name, object_data = me)
scn.objects.link(ob)
else:
ob = bpy.data.objects[p_name]
ob.data = me
scn.update()
return ob
def create_vertice_shapes(angles):
bm = bmesh.new()
for a in angles:
bm.verts.new((a[0], a[1], 0))
bm.verts.new((0, 0, 0))
ob = create_mesh_obj(bm)
bm.free()
# basis = ob.shape_key_add(from_mix=False)
# basis.name = "basis"
return ob
ob = create_vertice_shapes(angles)
ob.name = object_name
The script give us an arc of vertices and a center vertices. Make a face. F
The second snippet contains the method to detect collision using the bvhtree from the mathutils module. Make sure, the objects name is correct.
Uncommenting one the last three lines will give us a certain result.
bpy.app.handlers.scene_update_pre.append(my_handler)
This line registers the function as a handler on scene update. After uncommenting this line and running the script you will see a live update of the calculation. Depending on your scene this will be computationally expensive.
bpy.app.handlers.frame_change_pre.append(my_handler)
This line registers the function as a handler on framechange. Useful, if you only want to see the update on playback to view the animation, or if you want to render the animation.
my_handler(bpy.context.scene)
This line executes the function immediately, and only once.
import bpy
import bmesh
from mathutils.bvhtree import BVHTree
import mathutils
import math
import numpy as np
EPSILON = 0.00001
MAXIMUM_DISTANCE = 5
object_name = 'light_plane'
radius_start = math.pi*1/4
radius_end = math.pi*3/4
radius_step = math.pi/256
angles = [(math.cos(a), math.sin(a)) for a in np.arange(radius_start, radius_end + 0.00001, radius_step)]
print(angles)
def set_positions(ob, angles, obstacles):
trees = []
for obstacle in obstacles:
bm = bmesh.new()
bm.from_object(obstacle, bpy.context.scene)
bmesh.ops.transform(bm, matrix=obstacle.matrix_world, verts=bm.verts)
trees.append(BVHTree.FromBMesh(bm, epsilon=EPSILON))
for idx, a in enumerate(angles):
min_dist = MAXIMUM_DISTANCE
v_loc = mathutils.Vector((a[0], a[1], 0)) * MAXIMUM_DISTANCE
for bvh in trees:
v = (ob.matrix_world * mathutils.Vector((a[0], a[1], 0))) - ob.location
loc, no, i, d = bvh.ray_cast(ob.location, v)
if d is not None:
if d < min_dist:
min_dist = d
v_loc = ob.matrix_world.inverted() * loc
ob.data.vertices[idx].co = v_loc
def my_handler(scene):
ob = bpy.data.objects[object_name]
selection = [oo for oo in bpy.data.objects if oo != ob]
print(ob.location)
set_positions(ob, angles, selection)
for h in bpy.app.handlers.scene_update_pre:
bpy.app.handlers.scene_update_pre.remove(h)
for h in bpy.app.handlers.frame_change_pre:
bpy.app.handlers.frame_change_pre.remove(h)
#bpy.app.handlers.scene_update_pre.append(my_handler)
#bpy.app.handlers.frame_change_pre.append(my_handler)
#my_handler(bpy.context.scene)