diff options
234 files changed, 4577 insertions, 3160 deletions
diff --git a/doc/doxygen/Doxyfile b/doc/doxygen/Doxyfile index 1e430823b10..6e4a087fa36 100644 --- a/doc/doxygen/Doxyfile +++ b/doc/doxygen/Doxyfile @@ -38,7 +38,7 @@ PROJECT_NAME = Blender # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = "V2.90" +PROJECT_NUMBER = "V2.91" # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a diff --git a/extern/cuew/src/cuew.c b/extern/cuew/src/cuew.c index f477ec48a18..7a1b0018a24 100644 --- a/extern/cuew/src/cuew.c +++ b/extern/cuew/src/cuew.c @@ -879,7 +879,7 @@ int cuewCompilerVersion(void) { } /* get --version output */ - strncat(command, "\"", 1); + strcat(command, "\""); strncat(command, path, sizeof(command) - 1); strncat(command, "\" --version", sizeof(command) - strlen(path) - 1); pipe = popen(command, "r"); diff --git a/intern/cycles/blender/blender_camera.cpp b/intern/cycles/blender/blender_camera.cpp index 592a69585de..e1ab3b3fbc1 100644 --- a/intern/cycles/blender/blender_camera.cpp +++ b/intern/cycles/blender/blender_camera.cpp @@ -76,6 +76,9 @@ struct BlenderCamera { int full_width; int full_height; + int render_width; + int render_height; + BoundBox2D border; BoundBox2D pano_viewplane; BoundBox2D viewport_camera_border; @@ -126,8 +129,10 @@ static void blender_camera_init(BlenderCamera *bcam, BL::RenderSettings &b_rende bcam->matrix = transform_identity(); /* render resolution */ - bcam->full_width = render_resolution_x(b_render); - bcam->full_height = render_resolution_y(b_render); + bcam->render_width = render_resolution_x(b_render); + bcam->render_height = render_resolution_y(b_render); + bcam->full_width = bcam->render_width; + bcam->full_height = bcam->render_height; } static float blender_camera_focal_distance(BL::RenderEngine &b_engine, @@ -398,8 +403,8 @@ static void blender_camera_sync(Camera *cam, /* panorama sensor */ if (bcam->type == CAMERA_PANORAMA && bcam->panorama_type == PANORAMA_FISHEYE_EQUISOLID) { - float fit_xratio = (float)bcam->full_width * bcam->pixelaspect.x; - float fit_yratio = (float)bcam->full_height * bcam->pixelaspect.y; + float fit_xratio = (float)bcam->render_width * bcam->pixelaspect.x; + float fit_yratio = (float)bcam->render_height * bcam->pixelaspect.y; bool horizontal_fit; float sensor_size; diff --git a/intern/cycles/blender/blender_session.cpp b/intern/cycles/blender/blender_session.cpp index 391a1b8f473..ca363802919 100644 --- a/intern/cycles/blender/blender_session.cpp +++ b/intern/cycles/blender/blender_session.cpp @@ -492,27 +492,15 @@ void BlenderSession::render(BL::Depsgraph &b_depsgraph_) /* Update denoising parameters. */ session->set_denoising(session_params.denoising); - bool use_denoising = session_params.denoising.use; - bool store_denoising_passes = session_params.denoising.store_passes; - - buffer_params.denoising_data_pass = use_denoising || store_denoising_passes; - buffer_params.denoising_clean_pass = (scene->film->denoising_flags & DENOISING_CLEAN_ALL_PASSES); - buffer_params.denoising_prefiltered_pass = store_denoising_passes && - session_params.denoising.type == DENOISER_NLM; - - scene->film->denoising_data_pass = buffer_params.denoising_data_pass; - scene->film->denoising_clean_pass = buffer_params.denoising_clean_pass; - scene->film->denoising_prefiltered_pass = buffer_params.denoising_prefiltered_pass; - - /* Add passes */ + /* Compute render passes and film settings. */ vector<Pass> passes = sync->sync_render_passes( b_rlay, b_view_layer, session_params.adaptive_sampling, session_params.denoising); - buffer_params.passes = passes; - scene->film->pass_alpha_threshold = b_view_layer.pass_alpha_threshold(); - scene->film->tag_passes_update(scene, passes); - scene->film->tag_update(scene); - scene->integrator->tag_update(scene); + /* Set buffer params, using film settings from sync_render_passes. */ + buffer_params.passes = passes; + buffer_params.denoising_data_pass = scene->film->denoising_data_pass; + buffer_params.denoising_clean_pass = scene->film->denoising_clean_pass; + buffer_params.denoising_prefiltered_pass = scene->film->denoising_prefiltered_pass; BL::RenderResult::views_iterator b_view_iter; diff --git a/intern/cycles/blender/blender_sync.cpp b/intern/cycles/blender/blender_sync.cpp index d509f2fc786..511061db08a 100644 --- a/intern/cycles/blender/blender_sync.cpp +++ b/intern/cycles/blender/blender_sync.cpp @@ -697,6 +697,16 @@ vector<Pass> BlenderSync::sync_render_passes(BL::RenderLayer &b_rlay, } RNA_END; + scene->film->denoising_data_pass = denoising.use || denoising.store_passes; + scene->film->denoising_clean_pass = (scene->film->denoising_flags & DENOISING_CLEAN_ALL_PASSES); + scene->film->denoising_prefiltered_pass = denoising.store_passes && + denoising.type == DENOISER_NLM; + + scene->film->pass_alpha_threshold = b_view_layer.pass_alpha_threshold(); + scene->film->tag_passes_update(scene, passes); + scene->film->tag_update(scene); + scene->integrator->tag_update(scene); + return passes; } diff --git a/release/datafiles/userdef/userdef_default.c b/release/datafiles/userdef/userdef_default.c index fbdb226ab6d..df1be29f642 100644 --- a/release/datafiles/userdef/userdef_default.c +++ b/release/datafiles/userdef/userdef_default.c @@ -109,7 +109,7 @@ const UserDef U_default = { .keyconfigstr = "blender", .undosteps = 32, .undomemory = 0, - .gp_manhattendist = 1, + .gp_manhattandist = 1, .gp_euclideandist = 2, .gp_eraser = 25, .gp_settings = 0, diff --git a/release/scripts/startup/bl_operators/__init__.py b/release/scripts/startup/bl_operators/__init__.py index c39a7afcff9..c927cc184a3 100644 --- a/release/scripts/startup/bl_operators/__init__.py +++ b/release/scripts/startup/bl_operators/__init__.py @@ -46,7 +46,6 @@ _modules = [ "userpref", "uvcalc_follow_active", "uvcalc_lightmap", - "uvcalc_smart_project", "vertexpaint_dirt", "view3d", "gpencil_mesh_bake", diff --git a/release/scripts/startup/bl_operators/object_quick_effects.py b/release/scripts/startup/bl_operators/object_quick_effects.py index 311631ac65f..d0344b88be8 100644 --- a/release/scripts/startup/bl_operators/object_quick_effects.py +++ b/release/scripts/startup/bl_operators/object_quick_effects.py @@ -561,9 +561,61 @@ class QuickLiquid(Operator): return {'FINISHED'} +class QuickParticles(Operator): + """Use active object as particle emitter""" + bl_idname = "object.quick_particles" + bl_label = "Quick Particles" + + @classmethod + def poll(cls, context): + if not context.preferences.experimental.use_new_particle_system: + return False + if context.mode != 'OBJECT': + return False + if context.active_object is None: + return False + if context.active_object.type != 'MESH': + return False + return True + + def execute(self, context): + pointcloud = bpy.data.pointclouds.new("Particles") + pointcloud_object = bpy.data.objects.new("Particles", pointcloud) + modifier = pointcloud_object.modifiers.new("Simulation", 'SIMULATION') + simulation = bpy.data.simulations.new("Particle Simulation") + tree = simulation.node_tree + + default_name = "Particles" + particle_simulation_node = tree.nodes.new('SimulationNodeParticleSimulation') + particle_simulation_node.name = default_name + emitter_node = tree.nodes.new('SimulationNodeParticleMeshEmitter') + emitter_node.location.x -= 200 + emitter_node.location.y += 50 + emitter_node.inputs["Object"].default_value = context.active_object + force_node = tree.nodes.new('SimulationNodeForce') + force_node.location.x -= 200 + force_node.location.y -= 100 + force_node.inputs["Force"].default_value = (0, 0, -1) + + tree.links.new(particle_simulation_node.inputs["Emitters"], emitter_node.outputs["Emitter"]) + tree.links.new(particle_simulation_node.inputs["Forces"], force_node.outputs["Force"]) + + modifier.simulation = simulation + modifier.data_path = default_name + + for obj in context.selected_objects: + obj.select_set(False) + + context.collection.objects.link(pointcloud_object) + pointcloud_object.select_set(True) + context.view_layer.objects.active = pointcloud_object + pointcloud_object.show_bounds = True + return {'FINISHED'} + classes = ( QuickExplode, QuickFur, QuickSmoke, QuickLiquid, + QuickParticles, ) diff --git a/release/scripts/startup/bl_operators/userpref.py b/release/scripts/startup/bl_operators/userpref.py index 2e14df1920f..e92f493960a 100644 --- a/release/scripts/startup/bl_operators/userpref.py +++ b/release/scripts/startup/bl_operators/userpref.py @@ -119,8 +119,11 @@ class PREFERENCES_OT_copy_prev(Operator): # Find config folder from previous version. import os version = bpy.app.version + version_new = ((version[0] * 100) + version[1]) version_old = ((version[0] * 100) + version[1]) - 1 - while version_old % 10 > 0: + # Ensure we only try to copy files from a point release. + # The check below ensures the second numbers match. + while (version_new % 100) // 10 == (version_old % 100) // 10: version_split = version_old // 100, version_old % 100 if os.path.isdir(cls._old_version_path(version_split)): return version_split diff --git a/release/scripts/startup/bl_operators/uvcalc_smart_project.py b/release/scripts/startup/bl_operators/uvcalc_smart_project.py deleted file mode 100644 index 1f56cbe6d57..00000000000 --- a/release/scripts/startup/bl_operators/uvcalc_smart_project.py +++ /dev/null @@ -1,1053 +0,0 @@ -# ##### BEGIN GPL LICENSE BLOCK ##### -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# ##### END GPL LICENSE BLOCK ##### - -# TODO <pep8 compliant> - -from mathutils import ( - Matrix, - Vector, - geometry, -) -import bpy -from bpy.types import Operator - -DEG_TO_RAD = 0.017453292519943295 # pi/180.0 -# see bugs: -# - T31598 (when too small). -# - T48086 (when too big). -SMALL_NUM = 1e-12 - - -global USER_FILL_HOLES -global USER_FILL_HOLES_QUALITY -USER_FILL_HOLES = None -USER_FILL_HOLES_QUALITY = None - - -def pointInTri2D(v, v1, v2, v3): - key = v1.x, v1.y, v2.x, v2.y, v3.x, v3.y - - # Commented because its slower to do the bounds check, we should really cache the bounds info for each face. - ''' - # BOUNDS CHECK - xmin= 1000000 - ymin= 1000000 - - xmax= -1000000 - ymax= -1000000 - - for i in (0,2,4): - x= key[i] - y= key[i+1] - - if xmax<x: xmax= x - if ymax<y: ymax= y - if xmin>x: xmin= x - if ymin>y: ymin= y - - x= v.x - y= v.y - - if x<xmin or x>xmax or y < ymin or y > ymax: - return False - # Done with bounds check - ''' - try: - mtx = dict_matrix[key] - if not mtx: - return False - except: - side1 = v2 - v1 - side2 = v3 - v1 - - nor = side1.cross(side2) - - mtx = Matrix((side1, side2, nor)) - - # Zero area 2d tri, even tho we throw away zero area faces - # the projection UV can result in a zero area UV. - if not mtx.determinant(): - dict_matrix[key] = None - return False - - mtx.invert() - - dict_matrix[key] = mtx - - uvw = (v - v1) @ mtx - return 0 <= uvw[0] and 0 <= uvw[1] and uvw[0] + uvw[1] <= 1 - - -def boundsIsland(faces): - minx = maxx = faces[0].uv[0][0] # Set initial bounds. - miny = maxy = faces[0].uv[0][1] - # print len(faces), minx, maxx, miny , maxy - for f in faces: - for uv in f.uv: - x = uv.x - y = uv.y - if x < minx: - minx = x - if y < miny: - miny = y - if x > maxx: - maxx = x - if y > maxy: - maxy = y - - return minx, miny, maxx, maxy - - -""" -def boundsEdgeLoop(edges): - minx = maxx = edges[0][0] # Set initial bounds. - miny = maxy = edges[0][1] - # print len(faces), minx, maxx, miny , maxy - for ed in edges: - for pt in ed: - x= pt[0] - y= pt[1] - if x<minx: x= minx - if y<miny: y= miny - if x>maxx: x= maxx - if y>maxy: y= maxy - - return minx, miny, maxx, maxy -""" - -# Turns the islands into a list of unpordered edges (Non internal) -# Only for UV's -# only returns outline edges for intersection tests. and unique points. - - -def island2Edge(island): - - # Vert index edges - edges = {} - - unique_points = {} - - for f in island: - f_uvkey = list(map(tuple, f.uv)) - - for vIdx in range(len(f_uvkey)): - unique_points[f_uvkey[vIdx]] = f.uv[vIdx] - - if f.v[vIdx].index > f.v[vIdx - 1].index: - i1 = vIdx - 1 - i2 = vIdx - else: - i1 = vIdx - i2 = vIdx - 1 - - try: - edges[f_uvkey[i1], f_uvkey[i2]] *= 0 # sets any edge with more than 1 user to 0 are not returned. - except: - edges[f_uvkey[i1], f_uvkey[i2]] = (f.uv[i1] - f.uv[i2]).length - - # If 2 are the same then they will be together, but full [a,b] order is not correct. - - # Sort by length - length_sorted_edges = [(Vector(key[0]), Vector(key[1]), value) for key, value in edges.items() if value != 0] - - length_sorted_edges.sort(key=lambda a: -a[2]) # largest first - - # Its okay to leave the length in there. - # for e in length_sorted_edges: - # e.pop(2) - - # return edges and unique points - return length_sorted_edges, [v.to_3d() for v in unique_points.values()] - - -def pointInIsland(pt, island): - vec1, vec2, vec3 = Vector(), Vector(), Vector() - for f in island: - vec1.x, vec1.y = f.uv[0] - vec2.x, vec2.y = f.uv[1] - vec3.x, vec3.y = f.uv[2] - - if pointInTri2D(pt, vec1, vec2, vec3): - return True - - if len(f.v) == 4: - vec1.x, vec1.y = f.uv[0] - vec2.x, vec2.y = f.uv[2] - vec3.x, vec3.y = f.uv[3] - if pointInTri2D(pt, vec1, vec2, vec3): - return True - return False - - -# box is (left,bottom, right, top) -def islandIntersectUvIsland(source, target, SourceOffset): - # Is 1 point in the box, inside the vertLoops - edgeLoopsSource = source[6] # Pretend this is offset - edgeLoopsTarget = target[6] - - # Edge intersect test - for ed in edgeLoopsSource: - for seg in edgeLoopsTarget: - i = geometry.intersect_line_line_2d(seg[0], - seg[1], - SourceOffset + ed[0], - SourceOffset + ed[1], - ) - if i: - return 1 # LINE INTERSECTION - - # 1 test for source being totally inside target - SourceOffset.resize_3d() - for pv in source[7]: - if pointInIsland(pv + SourceOffset, target[0]): - return 2 # SOURCE INSIDE TARGET - - # 2 test for a part of the target being totally inside the source. - for pv in target[7]: - if pointInIsland(pv - SourceOffset, source[0]): - return 3 # PART OF TARGET INSIDE SOURCE. - - return 0 # NO INTERSECTION - - -def rotate_uvs(uv_points, angle): - - if angle != 0.0: - mat = Matrix.Rotation(angle, 2) - for uv in uv_points: - uv[:] = mat @ uv - - -def optiRotateUvIsland(faces): - uv_points = [uv for f in faces for uv in f.uv] - angle = geometry.box_fit_2d(uv_points) - - if angle != 0.0: - rotate_uvs(uv_points, angle) - - # orient them vertically (could be an option) - minx, miny, maxx, maxy = boundsIsland(faces) - w, h = maxx - minx, maxy - miny - # use epsilon so we don't randomly rotate (almost) perfect squares. - if h + 0.00001 < w: - from math import pi - angle = pi / 2.0 - rotate_uvs(uv_points, angle) - - -# Takes an island list and tries to find concave, hollow areas to pack smaller islands into. -def mergeUvIslands(islandList): - global USER_FILL_HOLES - global USER_FILL_HOLES_QUALITY - - # Pack islands to bottom LHS - # Sync with island - - # islandTotFaceArea = [] # A list of floats, each island area - # islandArea = [] # a list of tuples ( area, w,h) - - decoratedIslandList = [] - - islandIdx = len(islandList) - while islandIdx: - islandIdx -= 1 - minx, miny, maxx, maxy = boundsIsland(islandList[islandIdx]) - w, h = maxx - minx, maxy - miny - - totFaceArea = 0 - offset = Vector((minx, miny)) - for f in islandList[islandIdx]: - for uv in f.uv: - uv -= offset - - totFaceArea += f.area - - islandBoundsArea = w * h - efficiency = abs(islandBoundsArea - totFaceArea) - - # UV Edge list used for intersections as well as unique points. - edges, uniqueEdgePoints = island2Edge(islandList[islandIdx]) - - decoratedIslandList.append([ - islandList[islandIdx], - totFaceArea, - efficiency, - islandBoundsArea, - w, - h, - edges, - uniqueEdgePoints, - ]) - - # Sort by island bounding box area, smallest face area first. - # no.. chance that to most simple edge loop first. - decoratedIslandListAreaSort = decoratedIslandList[:] - - decoratedIslandListAreaSort.sort(key=lambda A: A[3]) - - # sort by efficiency, Least Efficient first. - decoratedIslandListEfficSort = decoratedIslandList[:] - # decoratedIslandListEfficSort.sort(lambda A, B: cmp(B[2], A[2])) - - decoratedIslandListEfficSort.sort(key=lambda A: -A[2]) - - # ================================================== THESE CAN BE TWEAKED. - # This is a quality value for the number of tests. - # from 1 to 4, generic quality value is from 1 to 100 - USER_STEP_QUALITY = ((USER_FILL_HOLES_QUALITY - 1) / 25.0) + 1 - - # If 100 will test as long as there is enough free space. - # this is rarely enough, and testing takes a while, so lower quality speeds this up. - - # 1 means they have the same quality - USER_FREE_SPACE_TO_TEST_QUALITY = 1 + (((100 - USER_FILL_HOLES_QUALITY) / 100.0) * 5) - - # print 'USER_STEP_QUALITY', USER_STEP_QUALITY - # print 'USER_FREE_SPACE_TO_TEST_QUALITY', USER_FREE_SPACE_TO_TEST_QUALITY - - removedCount = 0 - - areaIslandIdx = 0 - ctrl = Window.Qual.CTRL - BREAK = False - while areaIslandIdx < len(decoratedIslandListAreaSort) and not BREAK: - sourceIsland = decoratedIslandListAreaSort[areaIslandIdx] - # Already packed? - if not sourceIsland[0]: - areaIslandIdx += 1 - else: - efficIslandIdx = 0 - while efficIslandIdx < len(decoratedIslandListEfficSort) and not BREAK: - - if Window.GetKeyQualifiers() & ctrl: - BREAK = True - break - - # Now we have 2 islands, if the efficiency of the islands lowers there's an - # increasing likely hood that we can fit merge into the bigger UV island. - # this ensures a tight fit. - - # Just use figures we have about user/unused area to see if they might fit. - - targetIsland = decoratedIslandListEfficSort[efficIslandIdx] - - if sourceIsland[0] == targetIsland[0] or\ - not targetIsland[0] or\ - not sourceIsland[0]: - pass - else: - - #~ ([island, totFaceArea, efficiency, islandArea, w,h]) - # Wasted space on target is greater then UV bounding island area. - - #~ if targetIsland[3] > (sourceIsland[2]) and\ # - # ~ print USER_FREE_SPACE_TO_TEST_QUALITY - if targetIsland[2] > (sourceIsland[1] * USER_FREE_SPACE_TO_TEST_QUALITY) and\ - targetIsland[4] > sourceIsland[4] and\ - targetIsland[5] > sourceIsland[5]: - - # DEBUG # print '%.10f %.10f' % (targetIsland[3], sourceIsland[1]) - - # These enough spare space lets move the box until it fits - - # How many times does the source fit into the target x/y - blockTestXUnit = targetIsland[4] / sourceIsland[4] - blockTestYUnit = targetIsland[5] / sourceIsland[5] - - boxLeft = 0 - - # Distance we can move between whilst staying inside the targets bounds. - testWidth = targetIsland[4] - sourceIsland[4] - testHeight = targetIsland[5] - sourceIsland[5] - - # Increment we move each test. x/y - xIncrement = (testWidth / (blockTestXUnit * ((USER_STEP_QUALITY / 50) + 0.1))) - yIncrement = (testHeight / (blockTestYUnit * ((USER_STEP_QUALITY / 50) + 0.1))) - - # Make sure were not moving less then a 3rg of our width/height - if xIncrement < sourceIsland[4] / 3: - xIncrement = sourceIsland[4] - if yIncrement < sourceIsland[5] / 3: - yIncrement = sourceIsland[5] - - boxLeft = 0 # Start 1 back so we can jump into the loop. - boxBottom = 0 # -yIncrement - - # ~ testcount= 0 - - while boxBottom <= testHeight: - # Should we use this? - not needed for now. - # ~ if Window.GetKeyQualifiers() & ctrl: - # ~ BREAK= True - # ~ break - - # testcount+=1 - # print 'Testing intersect' - Intersect = islandIntersectUvIsland( - sourceIsland, targetIsland, Vector((boxLeft, boxBottom))) - # print 'Done', Intersect - if Intersect == 1: # Line intersect, don't bother with this any more - pass - - if Intersect == 2: # Source inside target - """ - We have an intersection, if we are inside the target - then move us 1 whole width across, - Its possible this is a bad idea since 2 skinny Angular faces - could join without 1 whole move, but its a lot more optimal to speed this up - since we have already tested for it. - - It gives about 10% speedup with minimal errors. - """ - # Move the test along its width + SMALL_NUM - #boxLeft += sourceIsland[4] + SMALL_NUM - boxLeft += sourceIsland[4] - elif Intersect == 0: # No intersection?? Place it. - # Progress - removedCount += 1 -# XXX Window.DrawProgressBar(0.0, 'Merged: %i islands, Ctrl to finish early.' % removedCount) - - # Move faces into new island and offset - targetIsland[0].extend(sourceIsland[0]) - offset = Vector((boxLeft, boxBottom)) - - for f in sourceIsland[0]: - for uv in f.uv: - uv += offset - - del sourceIsland[0][:] # Empty - - # Move edge loop into new and offset. - # targetIsland[6].extend(sourceIsland[6]) - # while sourceIsland[6]: - targetIsland[6].extend([( - (e[0] + offset, e[1] + offset, e[2]) - ) for e in sourceIsland[6]]) - - del sourceIsland[6][:] # Empty - - # Sort by edge length, reverse so biggest are first. - - try: - targetIsland[6].sort(key=lambda A: A[2]) - except: - targetIsland[6].sort(lambda B, A: cmp(A[2], B[2])) - - targetIsland[7].extend(sourceIsland[7]) - offset = Vector((boxLeft, boxBottom, 0.0)) - for p in sourceIsland[7]: - p += offset - - del sourceIsland[7][:] - - # Decrement the efficiency - targetIsland[1] += sourceIsland[1] # Increment totFaceArea - targetIsland[2] -= sourceIsland[1] # Decrement efficiency - # IF we ever used these again, should set to 0, eg - sourceIsland[2] = 0 # No area if anyone wants to know - - break - - # INCREMENT NEXT LOCATION - if boxLeft > testWidth: - boxBottom += yIncrement - boxLeft = 0.0 - else: - boxLeft += xIncrement - # print testcount - - efficIslandIdx += 1 - areaIslandIdx += 1 - - # Remove empty islands - i = len(islandList) - while i: - i -= 1 - if not islandList[i]: - del islandList[i] # Can increment islands removed here. - -# Takes groups of faces. assumes face groups are UV groups. - - -def getUvIslands(faceGroups, me): - - # Get seams so we don't cross over seams - edge_seams = {} # should be a set - for ed in me.edges: - if ed.use_seam: - edge_seams[ed.key] = None # dummy var- use sets! - # Done finding seams - - islandList = [] - -# XXX Window.DrawProgressBar(0.0, 'Splitting %d projection groups into UV islands:' % len(faceGroups)) - # print '\tSplitting %d projection groups into UV islands:' % len(faceGroups), - # Find grouped faces - - faceGroupIdx = len(faceGroups) - - while faceGroupIdx: - faceGroupIdx -= 1 - faces = faceGroups[faceGroupIdx] - - if not faces: - continue - - # Build edge dict - edge_users = {} - - for i, f in enumerate(faces): - for ed_key in f.edge_keys: - if ed_key in edge_seams: # DELIMIT SEAMS! ;) - edge_users[ed_key] = [] # so as not to raise an error - else: - try: - edge_users[ed_key].append(i) - except: - edge_users[ed_key] = [i] - - # Modes - # 0 - face not yet touched. - # 1 - added to island list, and need to search - # 2 - touched and searched - don't touch again. - face_modes = [0] * len(faces) # initialize zero - untested. - - face_modes[0] = 1 # start the search with face 1 - - newIsland = [] - - newIsland.append(faces[0]) - - ok = True - while ok: - - ok = True - while ok: - ok = False - for i in range(len(faces)): - if face_modes[i] == 1: # search - for ed_key in faces[i].edge_keys: - for ii in edge_users[ed_key]: - if i != ii and face_modes[ii] == 0: - face_modes[ii] = ok = 1 # mark as searched - newIsland.append(faces[ii]) - - # mark as searched, don't look again. - face_modes[i] = 2 - - islandList.append(newIsland) - - ok = False - for i in range(len(faces)): - if face_modes[i] == 0: - newIsland = [] - newIsland.append(faces[i]) - - face_modes[i] = ok = 1 - break - # if not ok will stop looping - -# XXX Window.DrawProgressBar(0.1, 'Optimizing Rotation for %i UV Islands' % len(islandList)) - - for island in islandList: - optiRotateUvIsland(island) - - return islandList - - -def packIslands(islandList): - if USER_FILL_HOLES: - # XXX Window.DrawProgressBar(0.1, 'Merging Islands (Ctrl: skip merge)...') - mergeUvIslands(islandList) # Modify in place - - # Now we have UV islands, we need to pack them. - - # Make a synchronized list with the islands - # so we can box pack the islands. - packBoxes = [] - - # Keep a list of X/Y offset so we can save time by writing the - # uv's and packed data in one pass. - islandOffsetList = [] - - islandIdx = 0 - - while islandIdx < len(islandList): - minx, miny, maxx, maxy = boundsIsland(islandList[islandIdx]) - - w, h = maxx - minx, maxy - miny - - if USER_ISLAND_MARGIN: - minx -= USER_ISLAND_MARGIN * w / 2 - miny -= USER_ISLAND_MARGIN * h / 2 - maxx += USER_ISLAND_MARGIN * w / 2 - maxy += USER_ISLAND_MARGIN * h / 2 - - # recalc width and height - w, h = maxx - minx, maxy - miny - - if w < SMALL_NUM: - w = SMALL_NUM - if h < SMALL_NUM: - h = SMALL_NUM - - """Save the offset to be applied later, - we could apply to the UVs now and align them to the bottom left hand area - of the UV coords like the box packer imagines they are - but, its quicker just to remember their offset and - apply the packing and offset in 1 pass """ - islandOffsetList.append((minx, miny)) - - # Add to boxList. use the island idx for the BOX id. - packBoxes.append([0, 0, w, h]) - islandIdx += 1 - - # Now we have a list of boxes to pack that syncs - # with the islands. - - # print '\tPacking UV Islands...' -# XXX Window.DrawProgressBar(0.7, "Packing %i UV Islands..." % len(packBoxes) ) - - # time1 = time.time() - packWidth, packHeight = geometry.box_pack_2d(packBoxes) - - # print 'Box Packing Time:', time.time() - time1 - - # if len(packedLs) != len(islandList): - # raise ValueError("Packed boxes differs from original length") - - # print '\tWriting Packed Data to faces' -# XXX Window.DrawProgressBar(0.8, "Writing Packed Data to faces") - - # Sort by ID, so there in sync again - islandIdx = len(islandList) - # Having these here avoids divide by 0 - if islandIdx: - - if USER_STRETCH_ASPECT: - # Maximize to uv area?? Will write a normalize function. - xfactor = 1.0 / packWidth - yfactor = 1.0 / packHeight - else: - # Keep proportions. - xfactor = yfactor = 1.0 / max(packWidth, packHeight) - - while islandIdx: - islandIdx -= 1 - # Write the packed values to the UV's - - xoffset = packBoxes[islandIdx][0] - islandOffsetList[islandIdx][0] - yoffset = packBoxes[islandIdx][1] - islandOffsetList[islandIdx][1] - - for f in islandList[islandIdx]: # Offsetting the UV's so they fit in there packed box - for uv in f.uv: - uv.x = (uv.x + xoffset) * xfactor - uv.y = (uv.y + yoffset) * yfactor - - -def VectoQuat(vec): - vec = vec.normalized() - return vec.to_track_quat('Z', 'X' if abs(vec.x) > 0.5 else 'Y').inverted() - - -class thickface: - __slost__ = "v", "uv", "no", "area", "edge_keys" - - def __init__(self, face, uv_layer, mesh_verts): - self.v = [mesh_verts[i] for i in face.vertices] - self.uv = [uv_layer[i].uv for i in face.loop_indices] - - self.no = face.normal.copy() - self.area = face.area - self.edge_keys = face.edge_keys - - -def main_consts(): - from math import radians - - global ROTMAT_2D_POS_90D - global ROTMAT_2D_POS_45D - global RotMatStepRotation - - ROTMAT_2D_POS_90D = Matrix.Rotation(radians(90.0), 2) - ROTMAT_2D_POS_45D = Matrix.Rotation(radians(45.0), 2) - - RotMatStepRotation = [] - rot_angle = 22.5 # 45.0/2 - while rot_angle > 0.1: - RotMatStepRotation.append([ - Matrix.Rotation(radians(+rot_angle), 2), - Matrix.Rotation(radians(-rot_angle), 2), - ]) - - rot_angle = rot_angle / 2.0 - - -global ob -ob = None - - -def main(context, - island_margin, - projection_limit, - user_area_weight, - use_aspect, - stretch_to_bounds, - ): - global USER_FILL_HOLES - global USER_FILL_HOLES_QUALITY - global USER_STRETCH_ASPECT - global USER_ISLAND_MARGIN - - from math import cos - import time - - global dict_matrix - dict_matrix = {} - - # Constants: - # Takes a list of faces that make up a UV island and rotate - # until they optimally fit inside a square. - global ROTMAT_2D_POS_90D - global ROTMAT_2D_POS_45D - global RotMatStepRotation - main_consts() - - # Create the variables. - USER_PROJECTION_LIMIT = projection_limit - USER_ONLY_SELECTED_FACES = True - USER_SHARE_SPACE = 1 # Only for hole filling. - USER_STRETCH_ASPECT = stretch_to_bounds - USER_ISLAND_MARGIN = island_margin # Only for hole filling. - USER_FILL_HOLES = 0 - USER_FILL_HOLES_QUALITY = 50 # Only for hole filling. - USER_VIEW_INIT = 0 # Only for hole filling. - - is_editmode = (context.mode == 'EDIT_MESH') - if is_editmode: - obList = context.objects_in_mode_unique_data - else: - obList = [ - ob for ob in context.selected_editable_objects - if ob.type == 'MESH' and ob.data.library is None - ] - - if not is_editmode: - USER_ONLY_SELECTED_FACES = False - - if not obList: - raise Exception("error, no selected mesh objects") - - # Convert from being button types - USER_PROJECTION_LIMIT_CONVERTED = cos(USER_PROJECTION_LIMIT * DEG_TO_RAD) - USER_PROJECTION_LIMIT_HALF_CONVERTED = cos((USER_PROJECTION_LIMIT / 2) * DEG_TO_RAD) - - # Toggle Edit mode - if is_editmode: - bpy.ops.object.mode_set(mode='OBJECT') - # Assume face select mode! an annoying hack to toggle face select mode because Mesh doesn't like faceSelectMode. - - if USER_SHARE_SPACE: - # Sort by data name so we get consistent results - obList.sort(key=lambda ob: ob.data.name) - collected_islandList = [] - - time1 = time.time() - - # Tag as False so we don't operate on the same mesh twice. - for me in bpy.data.meshes: - me.tag = False - - for ob in obList: - me = ob.data - - if me.tag or me.library: - continue - - # Tag as used - me.tag = True - - if not me.uv_layers: # Mesh has no UV Coords, don't bother. - me.uv_layers.new() - - uv_layer = me.uv_layers.active.data - me_verts = list(me.vertices) - - if USER_ONLY_SELECTED_FACES: - meshFaces = [thickface(f, uv_layer, me_verts) for i, f in enumerate(me.polygons) if f.select] - else: - meshFaces = [thickface(f, uv_layer, me_verts) for i, f in enumerate(me.polygons)] - - # ======= - # Generate a projection list from face normals, this is meant to be smart :) - - # make a list of face props that are in sync with meshFaces - # Make a Face List that is sorted by area. - # meshFaces = [] - - # meshFaces.sort( lambda a, b: cmp(b.area , a.area) ) # Biggest first. - meshFaces.sort(key=lambda a: -a.area) - - # remove all zero area faces - while meshFaces and meshFaces[-1].area <= SMALL_NUM: - # Set their UV's to 0,0 - for uv in meshFaces[-1].uv: - uv.zero() - meshFaces.pop() - - if not meshFaces: - continue - - # Smallest first is slightly more efficient, - # but if the user cancels early then its better we work on the larger data. - - # Generate Projection Vecs - # 0d is 1.0 - # 180 IS -0.59846 - - # Initialize projectVecs - if USER_VIEW_INIT: - # Generate Projection - - # We add to this along the way - projectVecs = [Vector(Window.GetViewVector()) @ ob.matrix_world.inverted().to_3x3()] - else: - projectVecs = [] - - newProjectVec = meshFaces[0].no - newProjectMeshFaces = [] # Popping stuffs it up. - - # Pretend that the most unique angle is ages away to start the loop off - mostUniqueAngle = -1.0 - - # This is popped - tempMeshFaces = meshFaces[:] - - # This while only gathers projection vecs, faces are assigned later on. - while 1: - # If there's none there then start with the largest face - - # add all the faces that are close. - for fIdx in range(len(tempMeshFaces) - 1, -1, -1): - # Use half the angle limit so we don't overweight faces towards this - # normal and hog all the faces. - if newProjectVec.dot(tempMeshFaces[fIdx].no) > USER_PROJECTION_LIMIT_HALF_CONVERTED: - newProjectMeshFaces.append(tempMeshFaces.pop(fIdx)) - - # Add the average of all these faces normals as a projectionVec - averageVec = Vector((0.0, 0.0, 0.0)) - if user_area_weight == 0.0: - for fprop in newProjectMeshFaces: - averageVec += fprop.no - elif user_area_weight == 1.0: - for fprop in newProjectMeshFaces: - averageVec += fprop.no * fprop.area - else: - for fprop in newProjectMeshFaces: - averageVec += fprop.no * ((fprop.area * user_area_weight) + (1.0 - user_area_weight)) - - if averageVec.x != 0 or averageVec.y != 0 or averageVec.z != 0: # Avoid NAN - projectVecs.append(averageVec.normalized()) - - # Get the next vec! - # Pick the face that's most different to all existing angles :) - mostUniqueAngle = 1.0 # 1.0 is 0d. no difference. - mostUniqueIndex = 0 # dummy - - for fIdx in range(len(tempMeshFaces) - 1, -1, -1): - angleDifference = -1.0 # 180d difference. - - # Get the closest vec angle we are to. - for p in projectVecs: - temp_angle_diff = p.dot(tempMeshFaces[fIdx].no) - - if angleDifference < temp_angle_diff: - angleDifference = temp_angle_diff - - if angleDifference < mostUniqueAngle: - # We have a new most different angle - mostUniqueIndex = fIdx - mostUniqueAngle = angleDifference - - if mostUniqueAngle < USER_PROJECTION_LIMIT_CONVERTED: - # print 'adding', mostUniqueAngle, USER_PROJECTION_LIMIT, len(newProjectMeshFaces) - # Now weight the vector to all its faces, will give a more direct projection - # if the face its self was not representative of the normal from surrounding faces. - - newProjectVec = tempMeshFaces[mostUniqueIndex].no - newProjectMeshFaces = [tempMeshFaces.pop(mostUniqueIndex)] - - else: - if len(projectVecs) >= 1: # Must have at least 2 projections - break - - # If there are only zero area faces then its possible - # there are no projectionVecs - if not len(projectVecs): - Draw.PupMenu('error, no projection vecs where generated, 0 area faces can cause this.') - return - - faceProjectionGroupList = [[] for i in range(len(projectVecs))] - - # MAP and Arrange # We know there are 3 or 4 faces here - - for fIdx in range(len(meshFaces) - 1, -1, -1): - fvec = meshFaces[fIdx].no - i = len(projectVecs) - - # Initialize first - bestAng = fvec.dot(projectVecs[0]) - bestAngIdx = 0 - - # Cycle through the remaining, first already done - while i - 1: - i -= 1 - - newAng = fvec.dot(projectVecs[i]) - if newAng > bestAng: # Reverse logic for dotvecs - bestAng = newAng - bestAngIdx = i - - # Store the area for later use. - faceProjectionGroupList[bestAngIdx].append(meshFaces[fIdx]) - - # Cull faceProjectionGroupList, - - # Now faceProjectionGroupList is full of faces that face match the project Vecs list - for i in range(len(projectVecs)): - # Account for projectVecs having no faces. - if not faceProjectionGroupList[i]: - continue - - # Make a projection matrix from a unit length vector. - MatQuat = VectoQuat(projectVecs[i]) - - # Get the faces UV's from the projected vertex. - for f in faceProjectionGroupList[i]: - f_uv = f.uv - for j, v in enumerate(f.v): - f_uv[j][:] = (MatQuat @ v.co).xy - - if USER_SHARE_SPACE: - # Should we collect and pack later? - islandList = getUvIslands(faceProjectionGroupList, me) - collected_islandList.extend(islandList) - - else: - # Should we pack the islands for this 1 object? - islandList = getUvIslands(faceProjectionGroupList, me) - packIslands(islandList) - - # update the mesh here if we need to. - - # We want to pack all in 1 go, so pack now - if USER_SHARE_SPACE: - packIslands(collected_islandList) - - print("Smart Projection time: %.2f" % (time.time() - time1)) - - # aspect correction is only done in edit mode - and only smart unwrap supports currently - if is_editmode: - bpy.ops.object.mode_set(mode='EDIT') - - if use_aspect: - import bmesh - aspect = context.scene.uvedit_aspect(context.active_object) - if aspect[0] > aspect[1]: - aspect[0] = aspect[1] / aspect[0] - aspect[1] = 1.0 - else: - aspect[1] = aspect[0] / aspect[1] - aspect[0] = 1.0 - - bm = bmesh.from_edit_mesh(me) - - uv_act = bm.loops.layers.uv.active - - faces = [f for f in bm.faces if f.select] - - for f in faces: - for l in f.loops: - l[uv_act].uv[0] *= aspect[0] - l[uv_act].uv[1] *= aspect[1] - - dict_matrix.clear() - - -from bpy.props import FloatProperty, BoolProperty - - -class SmartProject(Operator): - """This script projection unwraps the selected faces of a mesh """ \ - """(it operates on all selected mesh objects, and can be used """ \ - """to unwrap selected faces, or all faces)""" - bl_idname = "uv.smart_project" - bl_label = "Smart UV Project" - bl_options = {'REGISTER', 'UNDO'} - - angle_limit: FloatProperty( - name="Angle Limit", - description="Lower for more projection groups, higher for less distortion", - min=1.0, max=89.0, - default=66.0, - ) - island_margin: FloatProperty( - name="Island Margin", - description="Margin to reduce bleed from adjacent islands", - min=0.0, max=1.0, - default=0.0, - ) - user_area_weight: FloatProperty( - name="Area Weight", - description="Weight projections vector by faces with larger areas", - min=0.0, max=1.0, - default=0.0, - ) - use_aspect: BoolProperty( - name="Correct Aspect", - description="Map UVs taking image aspect ratio into account", - default=True, - ) - stretch_to_bounds: BoolProperty( - name="Stretch to UV Bounds", - description="Stretch the final output to texture bounds", - default=True, - ) - - @classmethod - def poll(cls, context): - return context.active_object is not None - - def execute(self, context): - main(context, - self.island_margin, - self.angle_limit, - self.user_area_weight, - self.use_aspect, - self.stretch_to_bounds, - ) - return {'FINISHED'} - - def invoke(self, context, _event): - wm = context.window_manager - return wm.invoke_props_dialog(self) - - -classes = ( - SmartProject, -) diff --git a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py index 4d6ddb55f24..f16528103ff 100644 --- a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py +++ b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py @@ -405,6 +405,7 @@ class GPENCIL_MT_cleanup(Menu): layout = self.layout layout.operator("gpencil.frame_clean_loose", text="Delete Loose Points") + layout.operator("gpencil.frame_clean_duplicate", text="Delete Duplicated Frames") if ob.mode != 'PAINT_GPENCIL': layout.operator("gpencil.stroke_merge_by_distance", text="Merge by Distance") diff --git a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py index e4cecf3378f..83b3a27154a 100644 --- a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py +++ b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py @@ -2385,9 +2385,8 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel): 'OBJECT': [ *_tools_default, - # Disable for 2.90 release. - # None, - # _tools_view3d_add, + None, + _tools_view3d_add, ], 'POSE': [ *_tools_default, @@ -2415,9 +2414,8 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel): ], 'EDIT_MESH': [ *_tools_default, - # Disable for 2.90 release. - # None, - # _tools_view3d_add, + None, + _tools_view3d_add, None, ( _defs_edit_mesh.extrude, diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index 8880d8c5378..0a1be2dc698 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -2751,6 +2751,8 @@ class VIEW3D_MT_object_quick_effects(Menu): layout.operator("object.quick_explode") layout.operator("object.quick_smoke") layout.operator("object.quick_liquid") + if _context.preferences.experimental.use_new_particle_system: + layout.operator("object.quick_particles") class VIEW3D_MT_object_showhide(Menu): diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index 60eb0194a9a..7321f4f6345 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -475,6 +475,12 @@ texture_node_categories = [ ]), ] +def not_implemented_node(idname): + NodeType = getattr(bpy.types, idname) + name = NodeType.bl_rna.name + label = f"{name} (mockup)" + return NodeItem(idname, label=label) + simulation_node_categories = [ # Simulation Nodes SimulationNodeCategory("SIM_OUTPUT", "Output", items=[ @@ -489,12 +495,12 @@ simulation_node_categories = [ ]), SimulationNodeCategory("SIM_EMITTERS", "Emitters", items=[ NodeItem("SimulationNodeParticleMeshEmitter"), - NodeItem("SimulationNodeEmitParticles"), + not_implemented_node("SimulationNodeEmitParticles"), ]), SimulationNodeCategory("SIM_EVENTS", "Events", items=[ NodeItem("SimulationNodeParticleBirthEvent"), NodeItem("SimulationNodeParticleTimeStepEvent"), - NodeItem("SimulationNodeParticleMeshCollisionEvent"), + not_implemented_node("SimulationNodeParticleMeshCollisionEvent"), ]), SimulationNodeCategory("SIM_FORCES", "Forces", items=[ NodeItem("SimulationNodeForce"), @@ -502,18 +508,18 @@ simulation_node_categories = [ SimulationNodeCategory("SIM_EXECUTE", "Execute", items=[ NodeItem("SimulationNodeSetParticleAttribute"), NodeItem("SimulationNodeExecuteCondition"), - NodeItem("SimulationNodeMultiExecute"), + not_implemented_node("SimulationNodeMultiExecute"), ]), SimulationNodeCategory("SIM_NOISE", "Noise", items=[ - NodeItem("ShaderNodeTexNoise"), - NodeItem("ShaderNodeTexWhiteNoise"), + not_implemented_node("ShaderNodeTexNoise"), + not_implemented_node("ShaderNodeTexWhiteNoise"), ]), SimulationNodeCategory("SIM_COLOR", "Color", items=[ - NodeItem("ShaderNodeMixRGB"), - NodeItem("ShaderNodeInvert"), - NodeItem("ShaderNodeHueSaturation"), - NodeItem("ShaderNodeGamma"), - NodeItem("ShaderNodeBrightContrast"), + not_implemented_node("ShaderNodeMixRGB"), + not_implemented_node("ShaderNodeInvert"), + not_implemented_node("ShaderNodeHueSaturation"), + not_implemented_node("ShaderNodeGamma"), + not_implemented_node("ShaderNodeBrightContrast"), ]), SimulationNodeCategory("SIM_CONVERTER", "Converter", items=[ NodeItem("ShaderNodeMapRange"), @@ -525,11 +531,11 @@ simulation_node_categories = [ NodeItem("ShaderNodeCombineRGB"), NodeItem("ShaderNodeSeparateXYZ"), NodeItem("ShaderNodeCombineXYZ"), - NodeItem("ShaderNodeSeparateHSV"), - NodeItem("ShaderNodeCombineHSV"), + not_implemented_node("ShaderNodeSeparateHSV"), + not_implemented_node("ShaderNodeCombineHSV"), NodeItem("FunctionNodeBooleanMath"), NodeItem("FunctionNodeFloatCompare"), - NodeItem("FunctionNodeSwitch"), + not_implemented_node("FunctionNodeSwitch"), NodeItem("FunctionNodeCombineStrings"), ]), SimulationNodeCategory("SIM_GROUP", "Group", items=node_group_items), diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c index 50a8223a84c..ff31878a929 100644 --- a/source/blender/blenfont/intern/blf_font.c +++ b/source/blender/blenfont/intern/blf_font.c @@ -194,6 +194,8 @@ static GPUTexture *blf_batch_cache_texture_load(void) int offset_x = bitmap_len_landed % tex_width; int offset_y = bitmap_len_landed / tex_width; + GPU_texture_bind(gc->texture, 0); + /* TODO(germano): Update more than one row in a single call. */ while (remain) { int remain_row = tex_width - offset_x; diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 77d2a973c50..800a3a426b7 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -32,15 +32,15 @@ extern "C" { */ /* Blender major and minor version. */ -#define BLENDER_VERSION 290 +#define BLENDER_VERSION 291 /* Blender patch version for bugfix releases. */ #define BLENDER_VERSION_PATCH 0 /** Blender release cycle stage: alpha/beta/rc/release. */ -#define BLENDER_VERSION_CYCLE beta +#define BLENDER_VERSION_CYCLE alpha /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 7 +#define BLENDER_FILE_SUBVERSION 0 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and show a warning if the file diff --git a/source/blender/blenkernel/BKE_persistent_data_handle.hh b/source/blender/blenkernel/BKE_persistent_data_handle.hh index 721132560e3..884e4c00766 100644 --- a/source/blender/blenkernel/BKE_persistent_data_handle.hh +++ b/source/blender/blenkernel/BKE_persistent_data_handle.hh @@ -85,45 +85,45 @@ class PersistentObjectHandle : public PersistentIDHandle { class PersistentDataHandleMap { private: - Map<int32_t, const ID *> id_by_handle_; - Map<const ID *, int32_t> handle_by_id_; + Map<int32_t, ID *> id_by_handle_; + Map<ID *, int32_t> handle_by_id_; public: - void add(int32_t handle, const ID &id) + void add(int32_t handle, ID &id) { BLI_assert(handle >= 0); handle_by_id_.add(&id, handle); id_by_handle_.add(handle, &id); } - PersistentIDHandle lookup(const ID *id) const + PersistentIDHandle lookup(ID *id) const { const int handle = handle_by_id_.lookup_default(id, -1); return PersistentIDHandle(handle); } - PersistentObjectHandle lookup(const Object *object) const + PersistentObjectHandle lookup(Object *object) const { - const int handle = handle_by_id_.lookup_default((const ID *)object, -1); + const int handle = handle_by_id_.lookup_default((ID *)object, -1); return PersistentObjectHandle(handle); } - const ID *lookup(const PersistentIDHandle &handle) const + ID *lookup(const PersistentIDHandle &handle) const { - const ID *id = id_by_handle_.lookup_default(handle.handle_, nullptr); + ID *id = id_by_handle_.lookup_default(handle.handle_, nullptr); return id; } - const Object *lookup(const PersistentObjectHandle &handle) const + Object *lookup(const PersistentObjectHandle &handle) const { - const ID *id = this->lookup((const PersistentIDHandle &)handle); + ID *id = this->lookup((const PersistentIDHandle &)handle); if (id == nullptr) { return nullptr; } if (GS(id->name) != ID_OB) { return nullptr; } - return (const Object *)id; + return (Object *)id; } }; diff --git a/source/blender/blenkernel/BKE_sequencer.h b/source/blender/blenkernel/BKE_sequencer.h index c32abbb41c6..2873a13d1f7 100644 --- a/source/blender/blenkernel/BKE_sequencer.h +++ b/source/blender/blenkernel/BKE_sequencer.h @@ -625,6 +625,10 @@ void BKE_sequencer_color_balance_apply(struct StripColorBalance *cb, void BKE_sequencer_all_free_anim_ibufs(struct Scene *scene, int cfra); bool BKE_sequencer_check_scene_recursion(struct Scene *scene, struct ReportList *reports); bool BKE_sequencer_render_loop_check(struct Sequence *seq_main, struct Sequence *seq); +void BKE_sequencer_flag_for_removal(struct Scene *scene, + struct ListBase *seqbase, + struct Sequence *seq); +void BKE_sequencer_remove_flagged_sequences(struct Scene *scene, struct ListBase *seqbase); #ifdef __cplusplus } diff --git a/source/blender/blenkernel/BKE_simulation.h b/source/blender/blenkernel/BKE_simulation.h index 5aa71b6381d..6cbe77e8de3 100644 --- a/source/blender/blenkernel/BKE_simulation.h +++ b/source/blender/blenkernel/BKE_simulation.h @@ -32,6 +32,7 @@ void *BKE_simulation_add(struct Main *bmain, const char *name); void BKE_simulation_data_update(struct Depsgraph *depsgraph, struct Scene *scene, struct Simulation *simulation); +void BKE_simulation_update_dependencies(struct Simulation *simulation, struct Main *bmain); SimulationState *BKE_simulation_state_add(Simulation *simulation, const char *type, diff --git a/source/blender/blenkernel/intern/CCGSubSurf.c b/source/blender/blenkernel/intern/CCGSubSurf.c index e035d5cbb68..8d8301362b3 100644 --- a/source/blender/blenkernel/intern/CCGSubSurf.c +++ b/source/blender/blenkernel/intern/CCGSubSurf.c @@ -32,8 +32,6 @@ #include "CCGSubSurf.h" #include "CCGSubSurf_intern.h" -#include "GPU_glew.h" - /***/ int BKE_ccg_gridsize(int level) diff --git a/source/blender/blenkernel/intern/curve.c b/source/blender/blenkernel/intern/curve.c index 2e1d2fd7f95..fb424b2d859 100644 --- a/source/blender/blenkernel/intern/curve.c +++ b/source/blender/blenkernel/intern/curve.c @@ -223,7 +223,7 @@ void BKE_curve_init(Curve *cu, const short curve_type) cu->vfont->id.us += 4; cu->str = MEM_malloc_arrayN(12, sizeof(unsigned char), "str"); BLI_strncpy(cu->str, "Text", 12); - cu->len = cu->len_wchar = cu->pos = 4; + cu->len = cu->len_char32 = cu->pos = 4; cu->strinfo = MEM_calloc_arrayN(12, sizeof(CharInfo), "strinfo new"); cu->totbox = cu->actbox = 1; cu->tb = MEM_calloc_arrayN(MAXTEXTBOX, sizeof(TextBox), "textbox"); @@ -5438,7 +5438,7 @@ void BKE_curve_material_index_remove(Curve *cu, int index) if (curvetype == OB_FONT) { struct CharInfo *info = cu->strinfo; int i; - for (i = cu->len_wchar - 1; i >= 0; i--, info++) { + for (i = cu->len_char32 - 1; i >= 0; i--, info++) { if (info->mat_nr && info->mat_nr >= index) { info->mat_nr--; } @@ -5462,7 +5462,7 @@ bool BKE_curve_material_index_used(Curve *cu, int index) if (curvetype == OB_FONT) { struct CharInfo *info = cu->strinfo; int i; - for (i = cu->len_wchar - 1; i >= 0; i--, info++) { + for (i = cu->len_char32 - 1; i >= 0; i--, info++) { if (info->mat_nr == index) { return true; } @@ -5488,7 +5488,7 @@ void BKE_curve_material_index_clear(Curve *cu) if (curvetype == OB_FONT) { struct CharInfo *info = cu->strinfo; int i; - for (i = cu->len_wchar - 1; i >= 0; i--, info++) { + for (i = cu->len_char32 - 1; i >= 0; i--, info++) { info->mat_nr = 0; } } @@ -5510,7 +5510,7 @@ bool BKE_curve_material_index_validate(Curve *cu) CharInfo *info = cu->strinfo; const int max_idx = max_ii(0, cu->totcol); /* OB_FONT use 1 as first mat index, not 0!!! */ int i; - for (i = cu->len_wchar - 1; i >= 0; i--, info++) { + for (i = cu->len_char32 - 1; i >= 0; i--, info++) { if (info->mat_nr > max_idx) { info->mat_nr = 0; is_valid = false; @@ -5558,7 +5558,7 @@ void BKE_curve_material_remap(Curve *cu, const unsigned int *remap, unsigned int } else { strinfo = cu->strinfo; - charinfo_len = cu->len_wchar; + charinfo_len = cu->len_char32; } for (i = 0; i <= charinfo_len; i++) { diff --git a/source/blender/blenkernel/intern/customdata.c b/source/blender/blenkernel/intern/customdata.c index 22d4af6fa39..7bf11d86a63 100644 --- a/source/blender/blenkernel/intern/customdata.c +++ b/source/blender/blenkernel/intern/customdata.c @@ -2643,11 +2643,13 @@ static CustomDataLayer *customData_add_layer__internal(CustomData *data, } if (alloctype == CD_DUPLICATE && layerdata) { - if (typeInfo->copy) { - typeInfo->copy(layerdata, newlayerdata, totelem); - } - else { - memcpy(newlayerdata, layerdata, (size_t)totelem * typeInfo->size); + if (totelem > 0) { + if (typeInfo->copy) { + typeInfo->copy(layerdata, newlayerdata, totelem); + } + else { + memcpy(newlayerdata, layerdata, (size_t)totelem * typeInfo->size); + } } } else if (alloctype == CD_DEFAULT) { diff --git a/source/blender/blenkernel/intern/fcurve_driver.c b/source/blender/blenkernel/intern/fcurve_driver.c index 10d804f437e..87cb77930f5 100644 --- a/source/blender/blenkernel/intern/fcurve_driver.c +++ b/source/blender/blenkernel/intern/fcurve_driver.c @@ -21,12 +21,6 @@ * \ingroup bke */ -// #include <float.h> -// #include <math.h> -// #include <stddef.h> -// #include <stdio.h> -// #include <string.h> - #include "MEM_guardedalloc.h" #include "DNA_anim_types.h" @@ -66,17 +60,19 @@ static ThreadMutex python_driver_lock = BLI_MUTEX_INITIALIZER; static CLG_LogRef LOG = {"bke.fcurve"}; -/* Driver Variables --------------------------- */ +/* -------------------------------------------------------------------- */ +/** \name Driver Variables + * \{ */ /* TypeInfo for Driver Variables (dvti) */ typedef struct DriverVarTypeInfo { - /* evaluation callback */ + /* Evaluation callback. */ float (*get_value)(ChannelDriver *driver, DriverVar *dvar); - /* allocation of target slots */ - int num_targets; /* number of target slots required */ - const char *target_names[MAX_DRIVER_TARGETS]; /* UI names that should be given to the slots */ - short target_flags[MAX_DRIVER_TARGETS]; /* flags defining the requirements for each slot */ + /* Allocation of target slots. */ + int num_targets; /* Number of target slots required. */ + const char *target_names[MAX_DRIVER_TARGETS]; /* UI names that should be given to the slots. */ + short target_flags[MAX_DRIVER_TARGETS]; /* Flags defining the requirements for each slot. */ } DriverVarTypeInfo; /* Macro to begin definitions */ @@ -85,7 +81,11 @@ typedef struct DriverVarTypeInfo { /* Macro to end definitions */ #define END_DVAR_TYPEDEF } -/* ......... */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Driver Target Utilities + * \{ */ static ID *dtar_id_ensure_proxy_from(ID *id) { @@ -107,14 +107,14 @@ static float dtar_get_prop_val(ChannelDriver *driver, DriverTarget *dtar) int index = -1; float value = 0.0f; - /* sanity check */ + /* Sanity check. */ if (ELEM(NULL, driver, dtar)) { return 0.0f; } id = dtar_id_ensure_proxy_from(dtar->id); - /* error check for missing pointer... */ + /* Error check for missing pointer. */ if (id == NULL) { if (G.debug & G_DEBUG) { CLOG_ERROR(&LOG, "driver has an invalid target to use (path = %s)", dtar->rna_path); @@ -125,12 +125,12 @@ static float dtar_get_prop_val(ChannelDriver *driver, DriverTarget *dtar) return 0.0f; } - /* get RNA-pointer for the ID-block given in target */ + /* Get RNA-pointer for the ID-block given in target. */ RNA_id_pointer_create(id, &id_ptr); - /* get property to read from, and get value as appropriate */ + /* Get property to read from, and get value as appropriate. */ if (!RNA_path_resolve_property_full(&id_ptr, dtar->rna_path, &ptr, &prop, &index)) { - /* path couldn't be resolved */ + /* Path couldn't be resolved. */ if (G.debug & G_DEBUG) { CLOG_ERROR(&LOG, "Driver Evaluation Error: cannot resolve target for %s -> %s", @@ -144,9 +144,9 @@ static float dtar_get_prop_val(ChannelDriver *driver, DriverTarget *dtar) } if (RNA_property_array_check(prop)) { - /* array */ + /* Array. */ if (index < 0 || index >= RNA_property_array_length(&ptr, prop)) { - /* out of bounds */ + /* Out of bounds. */ if (G.debug & G_DEBUG) { CLOG_ERROR(&LOG, "Driver Evaluation Error: array index is out of bounds for %s -> %s (%d)", @@ -175,7 +175,7 @@ static float dtar_get_prop_val(ChannelDriver *driver, DriverTarget *dtar) } } else { - /* not an array */ + /* Not an array. */ switch (RNA_property_type(prop)) { case PROP_BOOLEAN: value = (float)RNA_property_boolean_get(&ptr, prop); @@ -194,7 +194,7 @@ static float dtar_get_prop_val(ChannelDriver *driver, DriverTarget *dtar) } } - /* if we're still here, we should be ok... */ + /* If we're still here, we should be ok. */ dtar->flag &= ~DTAR_FLAG_INVALID; return value; } @@ -214,14 +214,14 @@ bool driver_get_variable_property(ChannelDriver *driver, ID *id; int index = -1; - /* sanity check */ + /* Sanity check. */ if (ELEM(NULL, driver, dtar)) { return false; } id = dtar_id_ensure_proxy_from(dtar->id); - /* error check for missing pointer... */ + /* Error check for missing pointer. */ if (id == NULL) { if (G.debug & G_DEBUG) { CLOG_ERROR(&LOG, "driver has an invalid target to use (path = %s)", dtar->rna_path); @@ -232,19 +232,19 @@ bool driver_get_variable_property(ChannelDriver *driver, return false; } - /* get RNA-pointer for the ID-block given in target */ + /* Get RNA-pointer for the ID-block given in target. */ RNA_id_pointer_create(id, &id_ptr); - /* get property to read from, and get value as appropriate */ + /* Get property to read from, and get value as appropriate. */ if (dtar->rna_path == NULL || dtar->rna_path[0] == '\0') { ptr = PointerRNA_NULL; - prop = NULL; /* ok */ + prop = NULL; /* OK. */ } else if (RNA_path_resolve_property_full(&id_ptr, dtar->rna_path, &ptr, &prop, &index)) { - /* ok */ + /* OK. */ } else { - /* path couldn't be resolved */ + /* Path couldn't be resolved. */ if (G.debug & G_DEBUG) { CLOG_ERROR(&LOG, "Driver Evaluation Error: cannot resolve target for %s -> %s", @@ -265,7 +265,7 @@ bool driver_get_variable_property(ChannelDriver *driver, *r_prop = prop; *r_index = index; - /* if we're still here, we should be ok... */ + /* If we're still here, we should be ok. */ dtar->flag &= ~DTAR_FLAG_INVALID; return true; } @@ -277,14 +277,14 @@ static short driver_check_valid_targets(ChannelDriver *driver, DriverVar *dvar) DRIVER_TARGETS_USED_LOOPER_BEGIN (dvar) { Object *ob = (Object *)dtar_id_ensure_proxy_from(dtar->id); - /* check if this target has valid data */ + /* Check if this target has valid data. */ if ((ob == NULL) || (GS(ob->id.name) != ID_OB)) { - /* invalid target, so will not have enough targets */ + /* Invalid target, so will not have enough targets. */ driver->flag |= DRIVER_FLAG_INVALID; dtar->flag |= DTAR_FLAG_INVALID; } else { - /* target seems to be OK now... */ + /* Target seems to be OK now. */ dtar->flag &= ~DTAR_FLAG_INVALID; valid_targets++; } @@ -294,21 +294,25 @@ static short driver_check_valid_targets(ChannelDriver *driver, DriverVar *dvar) return valid_targets; } -/* ......... */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Driver Variable Utilities + * \{ */ -/* evaluate 'single prop' driver variable */ +/* Evaluate 'single prop' driver variable. */ static float dvar_eval_singleProp(ChannelDriver *driver, DriverVar *dvar) { - /* just evaluate the first target slot */ + /* Just evaluate the first target slot. */ return dtar_get_prop_val(driver, &dvar->targets[0]); } -/* evaluate 'rotation difference' driver variable */ +/* Evaluate 'rotation difference' driver variable. */ static float dvar_eval_rotDiff(ChannelDriver *driver, DriverVar *dvar) { short valid_targets = driver_check_valid_targets(driver, dvar); - /* make sure we have enough valid targets to use - all or nothing for now... */ + /* Make sure we have enough valid targets to use - all or nothing for now. */ if (driver_check_valid_targets(driver, dvar) != 2) { if (G.debug & G_DEBUG) { CLOG_WARN(&LOG, @@ -324,31 +328,31 @@ static float dvar_eval_rotDiff(ChannelDriver *driver, DriverVar *dvar) /* NOTE: for now, these are all just worldspace */ for (int i = 0; i < 2; i++) { - /* get pointer to loc values to store in */ + /* Get pointer to loc values to store in. */ DriverTarget *dtar = &dvar->targets[i]; Object *ob = (Object *)dtar_id_ensure_proxy_from(dtar->id); bPoseChannel *pchan; - /* after the checks above, the targets should be valid here... */ + /* After the checks above, the targets should be valid here. */ BLI_assert((ob != NULL) && (GS(ob->id.name) == ID_OB)); - /* try to get posechannel */ + /* Try to get pose-channel. */ pchan = BKE_pose_channel_find_name(ob->pose, dtar->pchan_name); - /* check if object or bone */ + /* Check if object or bone. */ if (pchan) { - /* bone */ + /* Bone. */ mat[i] = pchan->pose_mat; } else { - /* object */ + /* Object. */ mat[i] = ob->obmat; } } float q1[4], q2[4], quat[4], angle; - /* use the final posed locations */ + /* Use the final posed locations. */ mat4_to_quat(q1, mat[0]); mat4_to_quat(q2, mat[1]); @@ -360,15 +364,18 @@ static float dvar_eval_rotDiff(ChannelDriver *driver, DriverVar *dvar) return (angle > (float)M_PI) ? (float)((2.0f * (float)M_PI) - angle) : (float)(angle); } -/* evaluate 'location difference' driver variable */ -/* TODO: this needs to take into account space conversions... */ +/** + * Evaluate 'location difference' driver variable. + * + * TODO: this needs to take into account space conversions. + */ static float dvar_eval_locDiff(ChannelDriver *driver, DriverVar *dvar) { float loc1[3] = {0.0f, 0.0f, 0.0f}; float loc2[3] = {0.0f, 0.0f, 0.0f}; short valid_targets = driver_check_valid_targets(driver, dvar); - /* make sure we have enough valid targets to use - all or nothing for now... */ + /* Make sure we have enough valid targets to use - all or nothing for now. */ if (valid_targets < dvar->num_targets) { if (G.debug & G_DEBUG) { CLOG_WARN(&LOG, @@ -381,72 +388,72 @@ static float dvar_eval_locDiff(ChannelDriver *driver, DriverVar *dvar) } /* SECOND PASS: get two location values */ - /* NOTE: for now, these are all just worldspace */ + /* NOTE: for now, these are all just world-space */ DRIVER_TARGETS_USED_LOOPER_BEGIN (dvar) { - /* get pointer to loc values to store in */ + /* Get pointer to loc values to store in. */ Object *ob = (Object *)dtar_id_ensure_proxy_from(dtar->id); bPoseChannel *pchan; float tmp_loc[3]; - /* after the checks above, the targets should be valid here... */ + /* After the checks above, the targets should be valid here. */ BLI_assert((ob != NULL) && (GS(ob->id.name) == ID_OB)); - /* try to get posechannel */ + /* Try to get pose-channel. */ pchan = BKE_pose_channel_find_name(ob->pose, dtar->pchan_name); - /* check if object or bone */ + /* Check if object or bone. */ if (pchan) { - /* bone */ + /* Bone. */ if (dtar->flag & DTAR_FLAG_LOCALSPACE) { if (dtar->flag & DTAR_FLAG_LOCAL_CONSTS) { float mat[4][4]; - /* extract transform just like how the constraints do it! */ + /* Extract transform just like how the constraints do it! */ copy_m4_m4(mat, pchan->pose_mat); BKE_constraint_mat_convertspace( ob, pchan, mat, CONSTRAINT_SPACE_POSE, CONSTRAINT_SPACE_LOCAL, false); - /* ... and from that, we get our transform */ + /* ... and from that, we get our transform. */ copy_v3_v3(tmp_loc, mat[3]); } else { - /* transform space (use transform values directly) */ + /* Transform space (use transform values directly). */ copy_v3_v3(tmp_loc, pchan->loc); } } else { - /* convert to worldspace */ + /* Convert to worldspace. */ copy_v3_v3(tmp_loc, pchan->pose_head); mul_m4_v3(ob->obmat, tmp_loc); } } else { - /* object */ + /* Object. */ if (dtar->flag & DTAR_FLAG_LOCALSPACE) { if (dtar->flag & DTAR_FLAG_LOCAL_CONSTS) { - /* XXX: this should practically be the same as transform space... */ + /* XXX: this should practically be the same as transform space. */ float mat[4][4]; - /* extract transform just like how the constraints do it! */ + /* Extract transform just like how the constraints do it! */ copy_m4_m4(mat, ob->obmat); BKE_constraint_mat_convertspace( ob, NULL, mat, CONSTRAINT_SPACE_WORLD, CONSTRAINT_SPACE_LOCAL, false); - /* ... and from that, we get our transform */ + /* ... and from that, we get our transform. */ copy_v3_v3(tmp_loc, mat[3]); } else { - /* transform space (use transform values directly) */ + /* Transform space (use transform values directly). */ copy_v3_v3(tmp_loc, ob->loc); } } else { - /* worldspace */ + /* World-space. */ copy_v3_v3(tmp_loc, ob->obmat[3]); } } - /* copy the location to the right place */ + /* Copy the location to the right place. */ if (tarIndex) { copy_v3_v3(loc2, tmp_loc); } @@ -456,13 +463,14 @@ static float dvar_eval_locDiff(ChannelDriver *driver, DriverVar *dvar) } DRIVER_TARGETS_LOOPER_END; - /* if we're still here, there should now be two targets to use, - * so just take the length of the vector between these points - */ + /* If we're still here, there should now be two targets to use, + * so just take the length of the vector between these points. */ return len_v3v3(loc1, loc2); } -/* evaluate 'transform channel' driver variable */ +/** + * Evaluate 'transform channel' driver variable. + */ static float dvar_eval_transChan(ChannelDriver *driver, DriverVar *dvar) { DriverTarget *dtar = &dvar->targets[0]; @@ -473,15 +481,15 @@ static float dvar_eval_transChan(ChannelDriver *driver, DriverVar *dvar) bool use_eulers = false; short rot_order = ROT_MODE_EUL; - /* check if this target has valid data */ + /* Check if this target has valid data. */ if ((ob == NULL) || (GS(ob->id.name) != ID_OB)) { - /* invalid target, so will not have enough targets */ + /* Invalid target, so will not have enough targets. */ driver->flag |= DRIVER_FLAG_INVALID; dtar->flag |= DTAR_FLAG_INVALID; return 0.0f; } else { - /* target should be valid now */ + /* Target should be valid now. */ dtar->flag &= ~DTAR_FLAG_INVALID; } @@ -495,7 +503,7 @@ static float dvar_eval_transChan(ChannelDriver *driver, DriverVar *dvar) * but #DTAR_FLAG_LOCAL_CONSTS is for all the common "corrective-shapes-for-limbs" situations. */ if (pchan) { - /* bone */ + /* Bone. */ if (pchan->rotmode > 0) { copy_v3_v3(oldEul, pchan->eul); rot_order = pchan->rotmode; @@ -504,16 +512,15 @@ static float dvar_eval_transChan(ChannelDriver *driver, DriverVar *dvar) if (dtar->flag & DTAR_FLAG_LOCALSPACE) { if (dtar->flag & DTAR_FLAG_LOCAL_CONSTS) { - /* just like how the constraints do it! */ + /* Just like how the constraints do it! */ copy_m4_m4(mat, pchan->pose_mat); BKE_constraint_mat_convertspace( ob, pchan, mat, CONSTRAINT_SPACE_POSE, CONSTRAINT_SPACE_LOCAL, false); } else { - /* specially calculate local matrix, since chan_mat is not valid + /* Specially calculate local matrix, since chan_mat is not valid * since it stores delta transform of pose_mat so that deforms work - * so it cannot be used here for "transform" space - */ + * so it cannot be used here for "transform" space. */ BKE_pchan_to_mat4(pchan, mat); } } @@ -523,7 +530,7 @@ static float dvar_eval_transChan(ChannelDriver *driver, DriverVar *dvar) } } else { - /* object */ + /* Object. */ if (ob->rotmode > 0) { copy_v3_v3(oldEul, ob->rot); rot_order = ob->rotmode; @@ -532,25 +539,25 @@ static float dvar_eval_transChan(ChannelDriver *driver, DriverVar *dvar) if (dtar->flag & DTAR_FLAG_LOCALSPACE) { if (dtar->flag & DTAR_FLAG_LOCAL_CONSTS) { - /* just like how the constraints do it! */ + /* Just like how the constraints do it! */ copy_m4_m4(mat, ob->obmat); BKE_constraint_mat_convertspace( ob, NULL, mat, CONSTRAINT_SPACE_WORLD, CONSTRAINT_SPACE_LOCAL, false); } else { - /* transforms to matrix */ + /* Transforms to matrix. */ BKE_object_to_mat4(ob, mat); } } else { - /* worldspace matrix - just the good-old one */ + /* World-space matrix - just the good-old one. */ copy_m4_m4(mat, ob->obmat); } } - /* check which transform */ + /* Check which transform. */ if (dtar->transChan >= MAX_DTAR_TRANSCHAN_TYPES) { - /* not valid channel */ + /* Not valid channel. */ return 0.0f; } else if (dtar->transChan == DTAR_TRANSCHAN_SCALE_AVG) { @@ -567,7 +574,7 @@ static float dvar_eval_transChan(ChannelDriver *driver, DriverVar *dvar) return len_v3(mat[dtar->transChan - DTAR_TRANSCHAN_SCALEX]); } else if (dtar->transChan >= DTAR_TRANSCHAN_ROTX) { - /* extract rotation as eulers (if needed) + /* Extract rotation as eulers (if needed) * - definitely if rotation order isn't eulers already * - if eulers, then we have 2 options: * a) decompose transform matrix as required, then try to make eulers from @@ -596,7 +603,7 @@ static float dvar_eval_transChan(ChannelDriver *driver, DriverVar *dvar) return quat[channel]; } else { - /* extract location and choose right axis */ + /* Extract location and choose right axis. */ return mat[3][dtar->transChan]; } } @@ -666,41 +673,45 @@ void BKE_driver_target_matrix_to_rot_channels( } } -/* ......... */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Driver Variable Type Info + * \{ */ /* Table of Driver Variable Type Info Data */ static DriverVarTypeInfo dvar_types[MAX_DVAR_TYPES] = { - BEGIN_DVAR_TYPEDEF(DVAR_TYPE_SINGLE_PROP) dvar_eval_singleProp, /* eval callback */ - 1, /* number of targets used */ + BEGIN_DVAR_TYPEDEF(DVAR_TYPE_SINGLE_PROP) dvar_eval_singleProp, /* Eval callback. */ + 1, /* Number of targets used. */ {"Property"}, /* UI names for targets */ - {0} /* flags */ + {0} /* Flags. */ END_DVAR_TYPEDEF, - BEGIN_DVAR_TYPEDEF(DVAR_TYPE_ROT_DIFF) dvar_eval_rotDiff, /* eval callback */ - 2, /* number of targets used */ + BEGIN_DVAR_TYPEDEF(DVAR_TYPE_ROT_DIFF) dvar_eval_rotDiff, /* Eval callback. */ + 2, /* Number of targets used. */ {"Object/Bone 1", "Object/Bone 2"}, /* UI names for targets */ {DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY, - DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY} /* flags */ + DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY} /* Flags. */ END_DVAR_TYPEDEF, - BEGIN_DVAR_TYPEDEF(DVAR_TYPE_LOC_DIFF) dvar_eval_locDiff, /* eval callback */ - 2, /* number of targets used */ + BEGIN_DVAR_TYPEDEF(DVAR_TYPE_LOC_DIFF) dvar_eval_locDiff, /* Eval callback. */ + 2, /* Number of targets used. */ {"Object/Bone 1", "Object/Bone 2"}, /* UI names for targets */ {DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY, - DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY} /* flags */ + DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY} /* Flags. */ END_DVAR_TYPEDEF, - BEGIN_DVAR_TYPEDEF(DVAR_TYPE_TRANSFORM_CHAN) dvar_eval_transChan, /* eval callback */ - 1, /* number of targets used */ + BEGIN_DVAR_TYPEDEF(DVAR_TYPE_TRANSFORM_CHAN) dvar_eval_transChan, /* Eval callback. */ + 1, /* Number of targets used. */ {"Object/Bone"}, /* UI names for targets */ - {DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY} /* flags */ + {DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY} /* Flags. */ END_DVAR_TYPEDEF, }; /* Get driver variable typeinfo */ static const DriverVarTypeInfo *get_dvar_typeinfo(int type) { - /* check if valid type */ + /* Check if valid type. */ if ((type >= 0) && (type < MAX_DVAR_TYPES)) { return &dvar_types[type]; } @@ -709,40 +720,44 @@ static const DriverVarTypeInfo *get_dvar_typeinfo(int type) } } -/* Driver API --------------------------------- */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Driver API + * \{ */ /* Perform actual freeing driver variable and remove it from the given list */ void driver_free_variable(ListBase *variables, DriverVar *dvar) { - /* sanity checks */ + /* Sanity checks. */ if (dvar == NULL) { return; } - /* free target vars + /* Free target vars: * - need to go over all of them, not just up to the ones that are used * currently, since there may be some lingering RNA paths from * previous users needing freeing */ DRIVER_TARGETS_LOOPER_BEGIN (dvar) { - /* free RNA path if applicable */ + /* Free RNA path if applicable. */ if (dtar->rna_path) { MEM_freeN(dtar->rna_path); } } DRIVER_TARGETS_LOOPER_END; - /* remove the variable from the driver */ + /* Remove the variable from the driver. */ BLI_freelinkN(variables, dvar); } /* Free the driver variable and do extra updates */ void driver_free_variable_ex(ChannelDriver *driver, DriverVar *dvar) { - /* remove and free the driver variable */ + /* Remove and free the driver variable. */ driver_free_variable(&driver->variables, dvar); - /* since driver variables are cached, the expression needs re-compiling too */ + /* Since driver variables are cached, the expression needs re-compiling too. */ BKE_driver_invalidate_expression(driver, false, true); } @@ -753,9 +768,9 @@ void driver_variables_copy(ListBase *dst_vars, const ListBase *src_vars) BLI_duplicatelist(dst_vars, src_vars); LISTBASE_FOREACH (DriverVar *, dvar, dst_vars) { - /* need to go over all targets so that we don't leave any dangling paths */ + /* Need to go over all targets so that we don't leave any dangling paths. */ DRIVER_TARGETS_LOOPER_BEGIN (dvar) { - /* make a copy of target's rna path if available */ + /* Make a copy of target's rna path if available. */ if (dtar->rna_path) { dtar->rna_path = MEM_dupallocN(dtar->rna_path); } @@ -769,25 +784,24 @@ void driver_change_variable_type(DriverVar *dvar, int type) { const DriverVarTypeInfo *dvti = get_dvar_typeinfo(type); - /* sanity check */ + /* Sanity check. */ if (ELEM(NULL, dvar, dvti)) { return; } - /* set the new settings */ + /* Set the new settings. */ dvar->type = type; dvar->num_targets = dvti->num_targets; - /* make changes to the targets based on the defines for these types - * NOTE: only need to make sure the ones we're using here are valid... - */ + /* Make changes to the targets based on the defines for these types. + * NOTE: only need to make sure the ones we're using here are valid. */ DRIVER_TARGETS_USED_LOOPER_BEGIN (dvar) { short flags = dvti->target_flags[tarIndex]; - /* store the flags */ + /* Store the flags. */ dtar->flag = flags; - /* object ID types only, or idtype not yet initialized */ + /* Object ID types only, or idtype not yet initialized. */ if ((flags & DTAR_FLAG_ID_OB_ONLY) || (dtar->idtype == 0)) { dtar->idtype = ID_OB; } @@ -804,12 +818,12 @@ void driver_variable_name_validate(DriverVar *dvar) '?', ':', ';', '<', '>', '{', '}', '[', ']', '|', ' ', '.', '\t', '\n', '\r', }; - /* sanity checks */ + /* Sanity checks. */ if (dvar == NULL) { return; } - /* clear all invalid-name flags */ + /* Clear all invalid-name flags. */ dvar->flag &= ~DVAR_ALL_INVALID_FLAGS; /* 0) Zero-length identifiers are not allowed */ @@ -870,16 +884,16 @@ DriverVar *driver_add_new_variable(ChannelDriver *driver) { DriverVar *dvar; - /* sanity checks */ + /* Sanity checks. */ if (driver == NULL) { return NULL; } - /* make a new variable */ + /* Make a new variable. */ dvar = MEM_callocN(sizeof(DriverVar), "DriverVar"); BLI_addtail(&driver->variables, dvar); - /* give the variable a 'unique' name */ + /* Give the variable a 'unique' name. */ strcpy(dvar->name, CTX_DATA_(BLT_I18NCONTEXT_ID_ACTION, "var")); BLI_uniquename(&driver->variables, dvar, @@ -888,13 +902,13 @@ DriverVar *driver_add_new_variable(ChannelDriver *driver) offsetof(DriverVar, name), sizeof(dvar->name)); - /* set the default type to 'single prop' */ + /* Set the default type to 'single prop'. */ driver_change_variable_type(dvar, DVAR_TYPE_SINGLE_PROP); - /* since driver variables are cached, the expression needs re-compiling too */ + /* Since driver variables are cached, the expression needs re-compiling too. */ BKE_driver_invalidate_expression(driver, false, true); - /* return the target */ + /* Return the target. */ return dvar; } @@ -904,20 +918,20 @@ void fcurve_free_driver(FCurve *fcu) ChannelDriver *driver; DriverVar *dvar, *dvarn; - /* sanity checks */ + /* Sanity checks. */ if (ELEM(NULL, fcu, fcu->driver)) { return; } driver = fcu->driver; - /* free driver targets */ + /* Free driver targets. */ for (dvar = driver->variables.first; dvar; dvar = dvarn) { dvarn = dvar->next; driver_free_variable_ex(driver, dvar); } #ifdef WITH_PYTHON - /* free compiled driver expression */ + /* Free compiled driver expression. */ if (driver->expr_comp) { BPY_DECREF(driver->expr_comp); } @@ -936,27 +950,31 @@ ChannelDriver *fcurve_copy_driver(const ChannelDriver *driver) { ChannelDriver *ndriver; - /* sanity checks */ + /* Sanity checks. */ if (driver == NULL) { return NULL; } - /* copy all data */ + /* Copy all data. */ ndriver = MEM_dupallocN(driver); ndriver->expr_comp = NULL; ndriver->expr_simple = NULL; - /* copy variables */ + /* Copy variables. */ - /* to get rid of refs to non-copied data (that's still used on original) */ + /* To get rid of refs to non-copied data (that's still used on original). */ BLI_listbase_clear(&ndriver->variables); driver_variables_copy(&ndriver->variables, &driver->variables); - /* return the new driver */ + /* Return the new driver. */ return ndriver; } -/* Driver Expression Evaluation --------------- */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Driver Expression Evaluation + * \{ */ /* Index constants for the expression parameter array. */ enum { @@ -1026,7 +1044,7 @@ static bool driver_evaluate_simple_expr(ChannelDriver *driver, return true; default: - /* arriving here means a bug, not user error */ + /* Arriving here means a bug, not user error. */ CLOG_ERROR(&LOG, "simple driver expression evaluation failed: '%s'", driver->expression); return false; } @@ -1135,22 +1153,25 @@ void BKE_driver_invalidate_expression(ChannelDriver *driver, #endif } -/* Driver Evaluation -------------------------- */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Driver Evaluation + * \{ */ /* Evaluate a Driver Variable to get a value that contributes to the final */ float driver_get_variable_value(ChannelDriver *driver, DriverVar *dvar) { const DriverVarTypeInfo *dvti; - /* sanity check */ + /* Sanity check. */ if (ELEM(NULL, driver, dvar)) { return 0.0f; } - /* call the relevant callbacks to get the variable value + /* Call the relevant callbacks to get the variable value * using the variable type info, storing the obtained value - * in dvar->curval so that drivers can be debugged - */ + * in `dvar->curval` so that drivers can be debugged. */ dvti = get_dvar_typeinfo(dvar->type); if (dvti && dvti->get_value) { @@ -1167,25 +1188,25 @@ static void evaluate_driver_sum(ChannelDriver *driver) { DriverVar *dvar; - /* check how many variables there are first (i.e. just one?) */ + /* Check how many variables there are first (i.e. just one?). */ if (BLI_listbase_is_single(&driver->variables)) { - /* just one target, so just use that */ + /* Just one target, so just use that. */ dvar = driver->variables.first; driver->curval = driver_get_variable_value(driver, dvar); return; } - /* more than one target, so average the values of the targets */ + /* More than one target, so average the values of the targets. */ float value = 0.0f; int tot = 0; - /* loop through targets, adding (hopefully we don't get any overflow!) */ + /* Loop through targets, adding (hopefully we don't get any overflow!). */ for (dvar = driver->variables.first; dvar; dvar = dvar->next) { value += driver_get_variable_value(driver, dvar); tot++; } - /* perform operations on the total if appropriate */ + /* Perform operations on the total if appropriate. */ if (driver->type == DRIVER_TYPE_AVERAGE) { driver->curval = tot ? (value / (float)tot) : 0.0f; } @@ -1199,34 +1220,34 @@ static void evaluate_driver_min_max(ChannelDriver *driver) DriverVar *dvar; float value = 0.0f; - /* loop through the variables, getting the values and comparing them to existing ones */ + /* Loop through the variables, getting the values and comparing them to existing ones. */ for (dvar = driver->variables.first; dvar; dvar = dvar->next) { - /* get value */ + /* Get value. */ float tmp_val = driver_get_variable_value(driver, dvar); - /* store this value if appropriate */ + /* Store this value if appropriate. */ if (dvar->prev) { - /* check if greater/smaller than the baseline */ + /* Check if greater/smaller than the baseline. */ if (driver->type == DRIVER_TYPE_MAX) { - /* max? */ + /* Max? */ if (tmp_val > value) { value = tmp_val; } } else { - /* min? */ + /* Min? */ if (tmp_val < value) { value = tmp_val; } } } else { - /* first item - make this the baseline for comparisons */ + /* First item - make this the baseline for comparisons. */ value = tmp_val; } } - /* store value in driver */ + /* Store value in driver. */ driver->curval = value; } @@ -1235,62 +1256,63 @@ static void evaluate_driver_python(PathResolvedRNA *anim_rna, ChannelDriver *driver_orig, const AnimationEvalContext *anim_eval_context) { - /* check for empty or invalid expression */ + /* Check for empty or invalid expression. */ if ((driver_orig->expression[0] == '\0') || (driver_orig->flag & DRIVER_FLAG_INVALID)) { driver->curval = 0.0f; } else if (!driver_try_evaluate_simple_expr( driver, driver_orig, &driver->curval, anim_eval_context->eval_time)) { #ifdef WITH_PYTHON - /* this evaluates the expression using Python, and returns its result: - * - on errors it reports, then returns 0.0f - */ + /* This evaluates the expression using Python, and returns its result: + * - on errors it reports, then returns 0.0f. */ BLI_mutex_lock(&python_driver_lock); driver->curval = BPY_driver_exec(anim_rna, driver, driver_orig, anim_eval_context); BLI_mutex_unlock(&python_driver_lock); -#else /* WITH_PYTHON*/ +#else /* WITH_PYTHON */ UNUSED_VARS(anim_rna, anim_eval_context); -#endif /* WITH_PYTHON*/ +#endif /* WITH_PYTHON */ } } -/* Evaluate an Channel-Driver to get a 'time' value to use instead of "evaltime" - * - "evaltime" is the frame at which F-Curve is being evaluated - * - has to return a float value - * - driver_orig is where we cache Python expressions, in case of COW +/** + * Evaluate an Channel-Driver to get a 'time' value to use + * instead of `anim_eval_context->eval_time`. + * + * - `anim_eval_context->eval_time` is the frame at which F-Curve is being evaluated. + * - Has to return a float value. + * - \a driver_orig is where we cache Python expressions, in case of COW */ float evaluate_driver(PathResolvedRNA *anim_rna, ChannelDriver *driver, ChannelDriver *driver_orig, const AnimationEvalContext *anim_eval_context) { - /* check if driver can be evaluated */ + /* Check if driver can be evaluated. */ if (driver_orig->flag & DRIVER_FLAG_INVALID) { return 0.0f; } switch (driver->type) { - case DRIVER_TYPE_AVERAGE: /* average values of driver targets */ - case DRIVER_TYPE_SUM: /* sum values of driver targets */ + case DRIVER_TYPE_AVERAGE: /* Average values of driver targets. */ + case DRIVER_TYPE_SUM: /* Sum values of driver targets. */ evaluate_driver_sum(driver); break; - case DRIVER_TYPE_MIN: /* smallest value */ - case DRIVER_TYPE_MAX: /* largest value */ + case DRIVER_TYPE_MIN: /* Smallest value. */ + case DRIVER_TYPE_MAX: /* Largest value. */ evaluate_driver_min_max(driver); break; - case DRIVER_TYPE_PYTHON: /* expression */ + case DRIVER_TYPE_PYTHON: /* Expression. */ evaluate_driver_python(anim_rna, driver, driver_orig, anim_eval_context); break; default: - /* special 'hack' - just use stored value + /* Special 'hack' - just use stored value * This is currently used as the mechanism which allows animated settings to be able - * to be changed via the UI. - */ + * to be changed via the UI. */ break; } - /* return value for driver */ + /* Return value for driver. */ return driver->curval; } diff --git a/source/blender/blenkernel/intern/font.c b/source/blender/blenkernel/intern/font.c index dfa5ff6975f..958acf0589b 100644 --- a/source/blender/blenkernel/intern/font.c +++ b/source/blender/blenkernel/intern/font.c @@ -797,7 +797,7 @@ static bool vfont_to_curve(Object *ob, } else { char32_t *mem_tmp; - slen = cu->len_wchar; + slen = cu->len_char32; /* Create unicode string */ mem_tmp = MEM_malloc_arrayN((slen + 1), sizeof(*mem_tmp), "convertedmem"); diff --git a/source/blender/blenkernel/intern/gpencil_geom.c b/source/blender/blenkernel/intern/gpencil_geom.c index 5b7753428f7..53bfd3aa8ff 100644 --- a/source/blender/blenkernel/intern/gpencil_geom.c +++ b/source/blender/blenkernel/intern/gpencil_geom.c @@ -2599,10 +2599,9 @@ void BKE_gpencil_stroke_set_random_color(bGPDstroke *gps) float color[4] = {1.0f, 1.0f, 1.0f, 1.0f}; bGPDspoint *pt = &gps->points[0]; - color[0] *= BLI_hash_int_01(BLI_hash_int_2d(gps->totpoints, pt->x)); - color[1] *= BLI_hash_int_01(BLI_hash_int_2d(gps->totpoints, pt->y)); - color[2] *= BLI_hash_int_01(BLI_hash_int_2d(gps->totpoints, pt->z)); - + color[0] *= BLI_hash_int_01(BLI_hash_int_2d(gps->totpoints / 5, pt->x + pt->z)); + color[1] *= BLI_hash_int_01(BLI_hash_int_2d(gps->totpoints + pt->x, pt->y * pt->z + pt->x)); + color[2] *= BLI_hash_int_01(BLI_hash_int_2d(gps->totpoints - pt->x, pt->z * pt->x + pt->y)); for (int i = 0; i < gps->totpoints; i++) { pt = &gps->points[i]; copy_v4_v4(pt->vert_color, color); diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index 9365ee040c2..042a5233978 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -57,6 +57,7 @@ #include "DNA_packedFile_types.h" #include "DNA_scene_types.h" #include "DNA_sequence_types.h" +#include "DNA_simulation_types.h" #include "DNA_world_types.h" #include "BLI_blenlib.h" @@ -3240,6 +3241,12 @@ static void image_walk_id_all_users( if (scene->nodetree && scene->use_nodes && !skip_nested_nodes) { image_walk_ntree_all_users(scene->nodetree, &scene->id, customdata, callback); } + break; + } + case ID_SIM: { + Simulation *simulation = (Simulation *)id; + image_walk_ntree_all_users(simulation->nodetree, &simulation->id, customdata, callback); + break; } default: break; @@ -3344,8 +3351,7 @@ static void image_free_tile(Image *ima, ImageTile *tile) for (int i = 0; i < TEXTARGET_COUNT; i++) { /* Only two textures depends on all tiles, so if this is a secondary tile we can keep the other * two. */ - if (tile != ima->tiles.first && - !(ELEM(i, TEXTARGET_TEXTURE_2D_ARRAY, TEXTARGET_TEXTURE_TILE_MAPPING))) { + if (tile != ima->tiles.first && !(ELEM(i, TEXTARGET_2D_ARRAY, TEXTARGET_TILE_MAPPING))) { continue; } @@ -3622,13 +3628,13 @@ ImageTile *BKE_image_add_tile(struct Image *ima, int tile_number, const char *la for (int eye = 0; eye < 2; eye++) { /* Reallocate GPU tile array. */ - if (ima->gputexture[TEXTARGET_TEXTURE_2D_ARRAY][eye] != NULL) { - GPU_texture_free(ima->gputexture[TEXTARGET_TEXTURE_2D_ARRAY][eye]); - ima->gputexture[TEXTARGET_TEXTURE_2D_ARRAY][eye] = NULL; + if (ima->gputexture[TEXTARGET_2D_ARRAY][eye] != NULL) { + GPU_texture_free(ima->gputexture[TEXTARGET_2D_ARRAY][eye]); + ima->gputexture[TEXTARGET_2D_ARRAY][eye] = NULL; } - if (ima->gputexture[TEXTARGET_TEXTURE_TILE_MAPPING][eye] != NULL) { - GPU_texture_free(ima->gputexture[TEXTARGET_TEXTURE_TILE_MAPPING][eye]); - ima->gputexture[TEXTARGET_TEXTURE_TILE_MAPPING][eye] = NULL; + if (ima->gputexture[TEXTARGET_TILE_MAPPING][eye] != NULL) { + GPU_texture_free(ima->gputexture[TEXTARGET_TILE_MAPPING][eye]); + ima->gputexture[TEXTARGET_TILE_MAPPING][eye] = NULL; } } diff --git a/source/blender/blenkernel/intern/lib_query.c b/source/blender/blenkernel/intern/lib_query.c index 00a42b12e07..0f81d45c10f 100644 --- a/source/blender/blenkernel/intern/lib_query.c +++ b/source/blender/blenkernel/intern/lib_query.c @@ -412,6 +412,8 @@ bool BKE_library_id_can_use_idtype(ID *id_owner, const short id_type_used) return ELEM(id_type_used, ID_MA); case ID_VO: return ELEM(id_type_used, ID_MA); + case ID_SIM: + return ELEM(id_type_used, ID_OB, ID_IM); case ID_IM: case ID_VF: case ID_TXT: @@ -422,7 +424,6 @@ bool BKE_library_id_can_use_idtype(ID *id_owner, const short id_type_used) case ID_PAL: case ID_PC: case ID_CF: - case ID_SIM: /* Those types never use/reference other IDs... */ return false; case ID_IP: diff --git a/source/blender/blenkernel/intern/node.c b/source/blender/blenkernel/intern/node.c index 20d65e52b09..8d7cb90fb71 100644 --- a/source/blender/blenkernel/intern/node.c +++ b/source/blender/blenkernel/intern/node.c @@ -61,6 +61,7 @@ #include "BKE_lib_query.h" #include "BKE_main.h" #include "BKE_node.h" +#include "BKE_simulation.h" #include "BLI_ghash.h" #include "BLI_threads.h" @@ -845,12 +846,12 @@ static void socket_id_user_increment(bNodeSocket *sock) switch ((eNodeSocketDatatype)sock->type) { case SOCK_OBJECT: { bNodeSocketValueObject *default_value = sock->default_value; - id_us_plus(&default_value->value->id); + id_us_plus((ID *)default_value->value); break; } case SOCK_IMAGE: { bNodeSocketValueImage *default_value = sock->default_value; - id_us_plus(&default_value->value->id); + id_us_plus((ID *)default_value->value); break; } case SOCK_FLOAT: @@ -2493,6 +2494,7 @@ ID *BKE_node_tree_find_owner_ID(Main *bmain, struct bNodeTree *ntree) &bmain->textures, &bmain->scenes, &bmain->linestyles, + &bmain->simulations, NULL}; for (int i = 0; lists[i] != NULL; i++) { @@ -3637,6 +3639,16 @@ void ntreeUpdateAllUsers(Main *main, ID *ngroup) FOREACH_NODETREE_END; } +static void ntreeUpdateSimulationDependencies(Main *main, bNodeTree *simulation_ntree) +{ + FOREACH_NODETREE_BEGIN (main, ntree, owner_id) { + if (GS(owner_id->name) == ID_SIM && ntree == simulation_ntree) { + BKE_simulation_update_dependencies((Simulation *)owner_id, main); + } + } + FOREACH_NODETREE_END; +} + void ntreeUpdateTree(Main *bmain, bNodeTree *ntree) { bNode *node; @@ -3679,7 +3691,6 @@ void ntreeUpdateTree(Main *bmain, bNodeTree *ntree) ntreeInterfaceTypeUpdate(ntree); } - /* XXX hack, should be done by depsgraph!! */ if (bmain) { ntreeUpdateAllUsers(bmain, &ntree->id); } @@ -3695,6 +3706,11 @@ void ntreeUpdateTree(Main *bmain, bNodeTree *ntree) ntree_validate_links(ntree); } + if (bmain != NULL && ntree->typeinfo == ntreeType_Simulation && + (ntree->id.flag & LIB_EMBEDDED_DATA)) { + ntreeUpdateSimulationDependencies(bmain, ntree); + } + /* clear update flags */ for (node = ntree->nodes.first; node; node = node->next) { node->update = 0; diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index b7fd6a50e6d..f77c79a2be7 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -2409,7 +2409,7 @@ static bool ob_parcurve(Object *ob, Object *par, float mat[4][4]) /* ctime is now a proper var setting of Curve which gets set by Animato like any other var * that's animated, but this will only work if it actually is animated. * - * We divide the curvetime calculated in the previous step by the length of the path, + * We divide the curve-time calculated in the previous step by the length of the path, * to get a time factor, which then gets clamped to lie within 0.0 - 1.0 range. */ if (cu->pathlen) { @@ -3964,15 +3964,20 @@ int BKE_object_is_modified(Scene *scene, Object *ob) return flag; } -/* Check of objects moves in time. */ -/* NOTE: This function is currently optimized for usage in combination - * with mti->canDeform, so modifiers can quickly check if their target - * objects moves (causing deformation motion blur) or not. +/** + * Check of objects moves in time. + * + * \note This function is currently optimized for usage in combination + * with modifier deformation checks (#eModifierTypeType_OnlyDeform), + * so modifiers can quickly check if their target objects moves + * (causing deformation motion blur) or not. * * This makes it possible to give some degree of false-positives here, * but it's currently an acceptable tradeoff between complexity and check * speed. In combination with checks of modifier stack and real life usage - * percentage of false-positives shouldn't be that height. + * percentage of false-positives shouldn't be that high. + * + * \note This function does not consider physics systems. */ bool BKE_object_moves_in_time(const Object *object, bool recurse_parent) { diff --git a/source/blender/blenkernel/intern/object_dupli.c b/source/blender/blenkernel/intern/object_dupli.c index f498e147110..413acde62d5 100644 --- a/source/blender/blenkernel/intern/object_dupli.c +++ b/source/blender/blenkernel/intern/object_dupli.c @@ -523,7 +523,7 @@ static void make_duplis_font(const DupliContext *ctx) /* Safety check even if it might fail badly when called for original object. */ const bool is_eval_curve = DEG_is_evaluated_id(&cu->id); - /* advance matching BLI_strncpy_wchar_from_utf8 */ + /* Advance matching BLI_str_utf8_as_utf32. */ for (a = 0; a < text_len; a++, ct++) { /* XXX That G.main is *really* ugly, but not sure what to do here... diff --git a/source/blender/blenkernel/intern/paint.c b/source/blender/blenkernel/intern/paint.c index d6fbba74a89..d4edcf88389 100644 --- a/source/blender/blenkernel/intern/paint.c +++ b/source/blender/blenkernel/intern/paint.c @@ -1691,7 +1691,6 @@ void BKE_sculpt_color_layer_create_if_needed(struct Object *object) CustomData_add_layer(&orig_me->vdata, CD_PROP_COLOR, CD_DEFAULT, NULL, orig_me->totvert); BKE_mesh_update_customdata_pointers(orig_me, true); DEG_id_tag_update(&orig_me->id, ID_RECALC_GEOMETRY); - return; } void BKE_sculpt_update_object_for_edit( diff --git a/source/blender/blenkernel/intern/sequencer.c b/source/blender/blenkernel/intern/sequencer.c index 42276b7f5c0..b16fc08a4af 100644 --- a/source/blender/blenkernel/intern/sequencer.c +++ b/source/blender/blenkernel/intern/sequencer.c @@ -6069,3 +6069,61 @@ bool BKE_sequencer_render_loop_check(Sequence *seq_main, Sequence *seq) return false; } + +static void sequencer_flag_users_for_removal(Scene *scene, ListBase *seqbase, Sequence *seq) +{ + LISTBASE_FOREACH (Sequence *, user_seq, seqbase) { + /* Look in metas for usage of seq. */ + if (user_seq->type == SEQ_TYPE_META) { + sequencer_flag_users_for_removal(scene, &user_seq->seqbase, seq); + } + + /* Clear seq from modifiers. */ + SequenceModifierData *smd; + for (smd = user_seq->modifiers.first; smd; smd = smd->next) { + if (smd->mask_sequence == seq) { + smd->mask_sequence = NULL; + } + } + + /* Remove effects, that use seq. */ + if ((user_seq->seq1 && user_seq->seq1 == seq) || (user_seq->seq2 && user_seq->seq2 == seq) || + (user_seq->seq3 && user_seq->seq3 == seq)) { + user_seq->flag |= SEQ_FLAG_DELETE; + /* Strips can be used as mask even if not in same seqbase. */ + sequencer_flag_users_for_removal(scene, &scene->ed->seqbase, user_seq); + } + } +} + +/* Flag seq and its users (effects) for removal. */ +void BKE_sequencer_flag_for_removal(Scene *scene, ListBase *seqbase, Sequence *seq) +{ + if (seq == NULL || (seq->flag & SEQ_FLAG_DELETE) != 0) { + return; + } + + /* Flag and remove meta children. */ + if (seq->type == SEQ_TYPE_META) { + LISTBASE_FOREACH (Sequence *, meta_child, &seq->seqbase) { + BKE_sequencer_flag_for_removal(scene, &seq->seqbase, meta_child); + } + } + + seq->flag |= SEQ_FLAG_DELETE; + sequencer_flag_users_for_removal(scene, seqbase, seq); +} + +/* Remove all flagged sequences, return true if sequence is removed. */ +void BKE_sequencer_remove_flagged_sequences(Scene *scene, ListBase *seqbase) +{ + LISTBASE_FOREACH_MUTABLE (Sequence *, seq, seqbase) { + if (seq->flag & SEQ_FLAG_DELETE) { + if (seq->type == SEQ_TYPE_META) { + BKE_sequencer_remove_flagged_sequences(scene, &seq->seqbase); + } + BLI_remlink(seqbase, seq); + BKE_sequence_free(scene, seq, true); + } + } +} diff --git a/source/blender/blenkernel/intern/simulation.cc b/source/blender/blenkernel/intern/simulation.cc index 95340e4e29c..c0fc8fcb464 100644 --- a/source/blender/blenkernel/intern/simulation.cc +++ b/source/blender/blenkernel/intern/simulation.cc @@ -112,8 +112,8 @@ static void simulation_copy_data(Main *bmain, ID *id_dst, const ID *id_src, cons BKE_simulation_state_copy_data(state_src, state_dst); } - BLI_duplicatelist(&simulation_dst->persistent_data_handles, - &simulation_src->persistent_data_handles); + BLI_listbase_clear(&simulation_dst->dependencies); + BLI_duplicatelist(&simulation_dst->dependencies, &simulation_src->dependencies); } static void simulation_free_data(ID *id) @@ -130,7 +130,7 @@ static void simulation_free_data(ID *id) BKE_simulation_state_remove_all(simulation); - BLI_freelistN(&simulation->persistent_data_handles); + BLI_freelistN(&simulation->dependencies); } static void simulation_foreach_id(ID *id, LibraryForeachIDData *data) @@ -140,9 +140,8 @@ static void simulation_foreach_id(ID *id, LibraryForeachIDData *data) /* nodetree **are owned by IDs**, treat them as mere sub-data and not real ID! */ BKE_library_foreach_ID_embedded(data, (ID **)&simulation->nodetree); } - LISTBASE_FOREACH ( - PersistentDataHandleItem *, handle_item, &simulation->persistent_data_handles) { - BKE_LIB_FOREACHID_PROCESS_ID(data, handle_item->id, IDWALK_CB_USER); + LISTBASE_FOREACH (SimulationDependency *, dependency, &simulation->dependencies) { + BKE_LIB_FOREACHID_PROCESS_ID(data, dependency->id, IDWALK_CB_USER); } } @@ -284,6 +283,14 @@ void BKE_simulation_data_update(Depsgraph *depsgraph, Scene *scene, Simulation * blender::sim::update_simulation_in_depsgraph(depsgraph, scene, simulation); } +void BKE_simulation_update_dependencies(Simulation *simulation, Main *bmain) +{ + bool dependencies_changed = blender::sim::update_simulation_dependencies(simulation); + if (dependencies_changed) { + DEG_relations_tag_update(bmain); + } +} + using StateTypeMap = blender::Map<std::string, std::unique_ptr<SimulationStateType>>; template<typename T> diff --git a/source/blender/blenlib/BLI_float3.hh b/source/blender/blenlib/BLI_float3.hh index b2633985ac7..a0979bc75bd 100644 --- a/source/blender/blenlib/BLI_float3.hh +++ b/source/blender/blenlib/BLI_float3.hh @@ -143,6 +143,17 @@ struct float3 { return normalize_v3(*this); } + /** + * Normalizes the vector in place. + */ + void normalize() + { + normalize_v3(*this); + } + + /** + * Returns a normalized vector. The original vector is not changed. + */ float3 normalized() const { float3 result; diff --git a/source/blender/blenlib/BLI_float4x4.hh b/source/blender/blenlib/BLI_float4x4.hh index 185cffd13ac..b4f12f17cc2 100644 --- a/source/blender/blenlib/BLI_float4x4.hh +++ b/source/blender/blenlib/BLI_float4x4.hh @@ -71,8 +71,8 @@ struct float4x4 { float4x4 inverted() const { - float result[4][4]; - invert_m4_m4(result, values); + float4x4 result; + invert_m4_m4(result.values, values); return result; } @@ -86,6 +86,18 @@ struct float4x4 { return this->inverted(); } + float4x4 transposed() const + { + float4x4 result; + transpose_m4_m4(result.values, values); + return result; + } + + float4x4 inverted_transposed_affine() const + { + return this->inverted_affine().transposed(); + } + struct float3x3_ref { const float4x4 &data; diff --git a/source/blender/blenlib/BLI_multi_value_map.hh b/source/blender/blenlib/BLI_multi_value_map.hh new file mode 100644 index 00000000000..c20c4ef9677 --- /dev/null +++ b/source/blender/blenlib/BLI_multi_value_map.hh @@ -0,0 +1,134 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __BLI_MULTI_VALUE_MAP_HH__ +#define __BLI_MULTI_VALUE_MAP_HH__ + +/** \file + * \ingroup bli + * + * A `blender::MultiValueMap<Key, Value>` is an unordered associative container that stores + * key-value pairs. It is different from `blender::Map` in that it can store multiple values for + * the same key. The list of values that corresponds to a specific key can contain duplicates + * and their order is maintained. + * + * This data structure is different from a `std::multi_map`, because multi_map can store the same + * key more than once and MultiValueMap can't. + * + * Currently, this class exists mainly for convenience. There are no performance benefits over + * using Map<Key, Vector<Value>>. In the future, a better implementation for this data structure + * can be developed. + */ + +#include "BLI_map.hh" +#include "BLI_vector.hh" + +namespace blender { + +template<typename Key, typename Value> class MultiValueMap { + private: + using MapType = Map<Key, Vector<Value>>; + MapType map_; + + public: + /** + * Add a new value for the given key. If the map contains the key already, the value will be + * appended to the list of corresponding values. + */ + void add(const Key &key, const Value &value) + { + this->add_as(key, value); + } + void add(const Key &key, Value &&value) + { + this->add_as(key, std::move(value)); + } + void add(Key &&key, const Value &value) + { + this->add_as(std::move(key), value); + } + void add(Key &&key, Value &&value) + { + this->add_as(std::move(key), std::move(value)); + } + template<typename ForwardKey, typename ForwardValue> + void add_as(ForwardKey &&key, ForwardValue &&value) + { + Vector<Value> &vector = map_.lookup_or_add_default_as(std::forward<ForwardKey>(key)); + vector.append(std::forward<ForwardValue>(value)); + } + + /** + * Add all given values to the key. + */ + void add_multiple(const Key &key, Span<Value> values) + { + this->add_multiple_as(key, values); + } + void add_multiple(Key &&key, Span<Value> values) + { + this->add_multiple_as(std::move(key), values); + } + template<typename ForwardKey> void add_multiple_as(ForwardKey &&key, Span<Value> values) + { + Vector<Value> &vector = map_.lookup_or_add_default_as(std::forward<ForwardKey>(key)); + vector.extend(values); + } + + /** + * Get a span to all the values that are stored for the given key. + */ + Span<Value> lookup(const Key &key) const + { + return this->lookup_as(key); + } + template<typename ForwardKey> Span<Value> lookup_as(const ForwardKey &key) const + { + const Vector<Value> *vector = map_.lookup_ptr_as(key); + if (vector != nullptr) { + return vector->as_span(); + } + return {}; + } + + /** + * Note: This signature will change when the implementation changes. + */ + typename MapType::ItemIterator items() const + { + return map_.items(); + } + + /** + * Note: This signature will change when the implementation changes. + */ + typename MapType::KeyIterator keys() const + { + return map_.keys(); + } + + /** + * Note: This signature will change when the implementation changes. + */ + typename MapType::ValueIterator values() const + { + return map_.values(); + } +}; + +} // namespace blender + +#endif /* __BLI_MULTI_VALUE_MAP_HH__ */ diff --git a/source/blender/blenlib/BLI_rand.hh b/source/blender/blenlib/BLI_rand.hh index 612ac0bbe19..7a98ee0f2bb 100644 --- a/source/blender/blenlib/BLI_rand.hh +++ b/source/blender/blenlib/BLI_rand.hh @@ -63,6 +63,15 @@ class RandomNumberGenerator { } /** + * \return Random value (0..N), but never N. + */ + int32_t get_int32(int32_t max_exclusive) + { + BLI_assert(max_exclusive > 0); + return this->get_int32() % max_exclusive; + } + + /** * \return Random value (0..1), but never 1.0. */ double get_double() @@ -78,6 +87,35 @@ class RandomNumberGenerator { return (float)this->get_int32() / 0x80000000; } + template<typename T> void shuffle(MutableSpan<T> values) + { + /* Cannot shuffle arrays of this size yet. */ + BLI_assert(values.size() <= INT32_MAX); + + for (int i = values.size() - 1; i >= 2; i--) { + int j = this->get_int32(i); + if (i != j) { + std::swap(values[i], values[j]); + } + } + } + + /** + * Compute uniformly distributed barycentric coordinates. + */ + float3 get_barycentric_coordinates() + { + float rand1 = this->get_float(); + float rand2 = this->get_float(); + + if (rand1 + rand2 > 1.0f) { + rand1 = 1.0f - rand1; + rand2 = 1.0f - rand2; + } + + return float3(rand1, rand2, 1.0f - rand1 - rand2); + } + float2 get_unit_float2(); float3 get_unit_float3(); float2 get_triangle_sample(float2 v1, float2 v2, float2 v3); diff --git a/source/blender/blenlib/BLI_resource_collector.hh b/source/blender/blenlib/BLI_resource_collector.hh index 10d610da618..e1be87d8af2 100644 --- a/source/blender/blenlib/BLI_resource_collector.hh +++ b/source/blender/blenlib/BLI_resource_collector.hh @@ -79,6 +79,12 @@ class ResourceCollector : NonCopyable, NonMovable { */ template<typename T> void add(destruct_ptr<T> resource, const char *name) { + /* There is no need to keep track of such types. */ + if (std::is_trivially_destructible_v<T>) { + resource.release(); + return; + } + BLI_assert(resource.get() != nullptr); this->add( resource.release(), diff --git a/source/blender/blenlib/BLI_span.hh b/source/blender/blenlib/BLI_span.hh index 2d875fe73be..81b86f647f6 100644 --- a/source/blender/blenlib/BLI_span.hh +++ b/source/blender/blenlib/BLI_span.hh @@ -87,7 +87,7 @@ namespace blender { */ template<typename T> class Span { private: - const T *start_ = nullptr; + const T *data_ = nullptr; int64_t size_ = 0; public: @@ -96,13 +96,13 @@ template<typename T> class Span { */ Span() = default; - Span(const T *start, int64_t size) : start_(start), size_(size) + Span(const T *start, int64_t size) : data_(start), size_(size) { BLI_assert(size >= 0); } template<typename U, typename std::enable_if_t<is_convertible_pointer_v<U, T>> * = nullptr> - Span(const U *start, int64_t size) : start_((const T *)start), size_(size) + Span(const U *start, int64_t size) : data_((const T *)start), size_(size) { BLI_assert(size >= 0); } @@ -136,7 +136,7 @@ template<typename T> class Span { * Span<Derived *> -> Span<Base *> */ template<typename U, typename std::enable_if_t<is_convertible_pointer_v<U, T>> * = nullptr> - Span(Span<U> array) : start_((T *)array.data()), size_(array.size()) + Span(Span<U> array) : data_((T *)array.data()), size_(array.size()) { } @@ -149,7 +149,7 @@ template<typename T> class Span { BLI_assert(start >= 0); BLI_assert(size >= 0); BLI_assert(start + size <= this->size() || size == 0); - return Span(start_ + start, size); + return Span(data_ + start, size); } Span slice(IndexRange range) const @@ -207,17 +207,17 @@ template<typename T> class Span { */ const T *data() const { - return start_; + return data_; } const T *begin() const { - return start_; + return data_; } const T *end() const { - return start_ + size_; + return data_ + size_; } /** @@ -228,7 +228,7 @@ template<typename T> class Span { { BLI_assert(index >= 0); BLI_assert(index < size_); - return start_[index]; + return data_[index]; } /** @@ -300,7 +300,7 @@ template<typename T> class Span { const T &first() const { BLI_assert(size_ > 0); - return start_[0]; + return data_[0]; } /** @@ -310,7 +310,7 @@ template<typename T> class Span { const T &last() const { BLI_assert(size_ > 0); - return start_[size_ - 1]; + return data_[size_ - 1]; } /** @@ -320,7 +320,7 @@ template<typename T> class Span { T get(int64_t index, const T &fallback) const { if (index < size_ && index >= 0) { - return start_[index]; + return data_[index]; } return fallback; } @@ -336,9 +336,9 @@ template<typename T> class Span { BLI_assert(size_ < 1000); for (int64_t i = 0; i < size_; i++) { - const T &value = start_[i]; + const T &value = data_[i]; for (int64_t j = i + 1; j < size_; j++) { - if (value == start_[j]) { + if (value == data_[j]) { return true; } } @@ -358,7 +358,7 @@ template<typename T> class Span { BLI_assert(size_ < 1000); for (int64_t i = 0; i < size_; i++) { - const T &value = start_[i]; + const T &value = data_[i]; if (other.contains(value)) { return true; } @@ -383,7 +383,7 @@ template<typename T> class Span { int64_t first_index_try(const T &search_value) const { for (int64_t i = 0; i < size_; i++) { - if (start_[i] == search_value) { + if (data_[i] == search_value) { return i; } } @@ -406,7 +406,7 @@ template<typename T> class Span { { BLI_assert((size_ * sizeof(T)) % sizeof(NewT) == 0); int64_t new_size = size_ * sizeof(T) / sizeof(NewT); - return Span<NewT>(reinterpret_cast<const NewT *>(start_), new_size); + return Span<NewT>(reinterpret_cast<const NewT *>(data_), new_size); } /** @@ -439,13 +439,13 @@ template<typename T> class Span { */ template<typename T> class MutableSpan { private: - T *start_; + T *data_; int64_t size_; public: MutableSpan() = default; - MutableSpan(T *start, const int64_t size) : start_(start), size_(size) + MutableSpan(T *start, const int64_t size) : data_(start), size_(size) { } @@ -459,7 +459,7 @@ template<typename T> class MutableSpan { operator Span<T>() const { - return Span<T>(start_, size_); + return Span<T>(data_, size_); } /** @@ -475,7 +475,7 @@ template<typename T> class MutableSpan { */ void fill(const T &value) { - initialized_fill_n(start_, size_, value); + initialized_fill_n(data_, size_, value); } /** @@ -486,7 +486,7 @@ template<typename T> class MutableSpan { { for (int64_t i : indices) { BLI_assert(i < size_); - start_[i] = value; + data_[i] = value; } } @@ -496,23 +496,23 @@ template<typename T> class MutableSpan { */ T *data() const { - return start_; + return data_; } T *begin() const { - return start_; + return data_; } T *end() const { - return start_ + size_; + return data_ + size_; } T &operator[](const int64_t index) const { BLI_assert(index < this->size()); - return start_[index]; + return data_[index]; } /** @@ -522,7 +522,7 @@ template<typename T> class MutableSpan { MutableSpan slice(const int64_t start, const int64_t length) const { BLI_assert(start + length <= this->size()); - return MutableSpan(start_ + start, length); + return MutableSpan(data_ + start, length); } /** @@ -571,7 +571,7 @@ template<typename T> class MutableSpan { */ Span<T> as_span() const { - return Span<T>(start_, size_); + return Span<T>(data_, size_); } /** @@ -590,7 +590,7 @@ template<typename T> class MutableSpan { T &last() const { BLI_assert(size_ > 0); - return start_[size_ - 1]; + return data_[size_ - 1]; } /** @@ -609,13 +609,24 @@ template<typename T> class MutableSpan { } /** + * Copy all values from another span into this span. This invokes undefined behavior when the + * destination contains uninitialized data and T is not trivially copy constructible. + * The size of both spans is expected to be the same. + */ + void copy_from(Span<T> values) + { + BLI_assert(size_ == values.size()); + initialized_copy_n(values.data(), size_, data_); + } + + /** * Returns a new span to the same underlying memory buffer. No conversions are done. */ template<typename NewT> MutableSpan<NewT> cast() const { BLI_assert((size_ * sizeof(T)) % sizeof(NewT) == 0); int64_t new_size = size_ * sizeof(T) / sizeof(NewT); - return MutableSpan<NewT>(reinterpret_cast<NewT *>(start_), new_size); + return MutableSpan<NewT>(reinterpret_cast<NewT *>(data_), new_size); } }; diff --git a/source/blender/blenlib/BLI_utildefines.h b/source/blender/blenlib/BLI_utildefines.h index bc35e969e6a..2699f2498ac 100644 --- a/source/blender/blenlib/BLI_utildefines.h +++ b/source/blender/blenlib/BLI_utildefines.h @@ -755,6 +755,43 @@ extern bool BLI_memory_is_zero(const void *arr, const size_t arr_size); /** \} */ /* -------------------------------------------------------------------- */ +/** \name C++ Macros + * \{ */ + +#ifdef __cplusplus + +/* Useful to port C code using enums to C++ where enums are strongly typed. + * To use after the enum declaration. */ +# define ENUM_OPERATORS(_enum_type) \ + inline constexpr _enum_type operator|(_enum_type a, _enum_type b) \ + { \ + return a = static_cast<_enum_type>(static_cast<int>(a) | b); \ + } \ + inline constexpr _enum_type operator&(_enum_type a, _enum_type b) \ + { \ + return a = static_cast<_enum_type>(static_cast<int>(a) & b); \ + } \ + inline constexpr _enum_type operator~(_enum_type a) \ + { \ + return a = static_cast<_enum_type>(~static_cast<int>(a)); \ + } \ + inline _enum_type &operator|=(_enum_type &a, _enum_type b) \ + { \ + return a = static_cast<_enum_type>(static_cast<int>(a) | b); \ + } \ + inline _enum_type &operator&=(_enum_type &a, _enum_type b) \ + { \ + return a = static_cast<_enum_type>(static_cast<int>(a) & b); \ + } + +#else +/* Output nothing. */ +# define ENUM_OPERATORS(_type) +#endif + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Misc Macros * \{ */ diff --git a/source/blender/blenlib/BLI_vector.hh b/source/blender/blenlib/BLI_vector.hh index 1bb674093bb..7eac511bf4a 100644 --- a/source/blender/blenlib/BLI_vector.hh +++ b/source/blender/blenlib/BLI_vector.hh @@ -561,7 +561,7 @@ class Vector { /** * Return a reference to the last element in the vector. - * This will assert when the vector is empty. + * This invokes undefined behavior when the vector is empty. */ const T &last() const { diff --git a/source/blender/blenlib/BLI_vector_adaptor.hh b/source/blender/blenlib/BLI_vector_adaptor.hh new file mode 100644 index 00000000000..cadffc0b445 --- /dev/null +++ b/source/blender/blenlib/BLI_vector_adaptor.hh @@ -0,0 +1,105 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __BLI_VECTOR_ADAPTOR_HH__ +#define __BLI_VECTOR_ADAPTOR_HH__ + +/** \file + * \ingroup bli + * + * A `blender::VectorAdaptor` is a container with a fixed maximum size and does not own the + * underlying memory. When an adaptor is constructed, you have to provide it with an uninitialized + * array that will be filled when elements are added to the vector. The vector adaptor is not able + * to grow. Therefore, it is undefined behavior to add more elements than fit into the provided + * buffer. + */ + +#include "BLI_span.hh" + +namespace blender { + +template<typename T> class VectorAdaptor { + private: + T *begin_; + T *end_; + T *capacity_end_; + + public: + VectorAdaptor() : begin_(nullptr), end_(nullptr), capacity_end_(nullptr) + { + } + + VectorAdaptor(T *data, int64_t capacity, int64_t size = 0) + : begin_(data), end_(data + size), capacity_end_(data + capacity) + { + } + + VectorAdaptor(MutableSpan<T> span) : VectorAdaptor(span.data(), span.size(), 0) + { + } + + void append(const T &value) + { + BLI_assert(end_ < capacity_end_); + new (end_) T(value); + end_++; + } + + void append(T &&value) + { + BLI_assert(end_ < capacity_end_); + new (end_) T(std::move(value)); + end_++; + } + + void append_n_times(const T &value, int64_t n) + { + BLI_assert(end_ + n <= capacity_end_); + uninitialized_fill_n(end_, n, value); + end_ += n; + } + + void extend(Span<T> values) + { + BLI_assert(end_ + values.size() <= capacity_end_); + uninitialized_copy_n(values.data(), values.size(), end_); + end_ += values.size(); + } + + int64_t capacity() const + { + return capacity_end_ - begin_; + } + + int64_t size() const + { + return end_ - begin_; + } + + bool is_empty() const + { + return begin_ == end_; + } + + bool is_full() const + { + return end_ == capacity_end_; + } +}; + +} // namespace blender + +#endif /* __BLI_VECTOR_ADAPTOR_HH__ */ diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index c6e04d4147a..4997917a93f 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -267,6 +267,7 @@ set(SRC BLI_utility_mixins.hh BLI_uvproject.h BLI_vector.hh + BLI_vector_adaptor.hh BLI_vector_set.hh BLI_vector_set_slots.hh BLI_vfontdata.h @@ -340,3 +341,29 @@ set_source_files_properties( ) blender_add_lib(bf_blenlib "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") + +if(WITH_GTESTS) + set(TEST_SRC + tests/BLI_array_test.cc + tests/BLI_disjoint_set_test.cc + tests/BLI_edgehash_test.cc + tests/BLI_index_mask_test.cc + tests/BLI_index_range_test.cc + tests/BLI_linear_allocator_test.cc + tests/BLI_map_test.cc + tests/BLI_math_base_safe_test.cc + tests/BLI_memory_utils_test.cc + tests/BLI_multi_value_map_test.cc + tests/BLI_set_test.cc + tests/BLI_span_test.cc + tests/BLI_stack_cxx_test.cc + tests/BLI_string_ref_test.cc + tests/BLI_vector_set_test.cc + tests/BLI_vector_test.cc + ) + set(TEST_LIB + bf_blenlib + ) + include(GTestTesting) + blender_add_test_lib(bf_bli_tests "${TEST_SRC}" "${INC};${TEST_INC}" "${INC_SYS}" "${LIB};${TEST_LIB}") +endif() diff --git a/tests/gtests/blenlib/BLI_array_test.cc b/source/blender/blenlib/tests/BLI_array_test.cc index c01ba26ffd7..7348a6f93f3 100644 --- a/tests/gtests/blenlib/BLI_array_test.cc +++ b/source/blender/blenlib/tests/BLI_array_test.cc @@ -4,7 +4,7 @@ #include "BLI_strict_flags.h" #include "testing/testing.h" -namespace blender { +namespace blender::tests { TEST(array, DefaultConstructor) { @@ -72,7 +72,7 @@ TEST(array, MoveConstructor) Array<int> array = {5, 6, 7, 8}; Array<int> new_array(std::move(array)); - EXPECT_EQ(array.size(), 0); + EXPECT_EQ(array.size(), 0); /* NOLINT: bugprone-use-after-move */ EXPECT_EQ(new_array.size(), 4); EXPECT_EQ(new_array[0], 5); EXPECT_EQ(new_array[1], 6); @@ -101,7 +101,7 @@ TEST(array, MoveAssignment) EXPECT_EQ(new_array.size(), 1); new_array = std::move(array); EXPECT_EQ(new_array.size(), 3); - EXPECT_EQ(array.size(), 0); + EXPECT_EQ(array.size(), 0); /* NOLINT: bugprone-use-after-move */ EXPECT_EQ(new_array[0], 1); EXPECT_EQ(new_array[1], 2); EXPECT_EQ(new_array[2], 3); @@ -141,7 +141,7 @@ TEST(array, NoInitializationSizeConstructor) using MyArray = Array<ConstructibleType>; TypedBuffer<MyArray> buffer; - memset(buffer, 100, sizeof(MyArray)); + memset((void *)&buffer, 100, sizeof(MyArray)); /* Doing this to avoid some compiler optimization. */ for (int64_t i : IndexRange(sizeof(MyArray))) { @@ -173,4 +173,4 @@ TEST(array, Fill) EXPECT_EQ(array[4], 3); } -} // namespace blender +} // namespace blender::tests diff --git a/tests/gtests/blenlib/BLI_disjoint_set_test.cc b/source/blender/blenlib/tests/BLI_disjoint_set_test.cc index 22663b936d9..f30ee610b2a 100644 --- a/tests/gtests/blenlib/BLI_disjoint_set_test.cc +++ b/source/blender/blenlib/tests/BLI_disjoint_set_test.cc @@ -5,7 +5,7 @@ #include "testing/testing.h" -namespace blender { +namespace blender::tests { TEST(disjoint_set, Test) { @@ -33,4 +33,4 @@ TEST(disjoint_set, Test) EXPECT_FALSE(disjoint_set.in_same_set(0, 4)); } -} // namespace blender +} // namespace blender::tests diff --git a/tests/gtests/blenlib/BLI_edgehash_test.cc b/source/blender/blenlib/tests/BLI_edgehash_test.cc index 23ad618825b..7106033df36 100644 --- a/tests/gtests/blenlib/BLI_edgehash_test.cc +++ b/source/blender/blenlib/tests/BLI_edgehash_test.cc @@ -5,10 +5,8 @@ #include <random> #include <vector> -extern "C" { #include "BLI_edgehash.h" #include "BLI_utildefines.h" -} #define VALUE_1 POINTER_FROM_INT(1) #define VALUE_2 POINTER_FROM_INT(2) @@ -334,10 +332,12 @@ TEST(edgehash, StressTest) /* check if the right ones have been removed */ for (int i = 0; i < shuffled.size(); i++) { bool haskey = BLI_edgehash_haskey(eh, shuffled[i].v1, shuffled[i].v2); - if (i < remove_until) + if (i < remove_until) { ASSERT_FALSE(haskey); - else + } + else { ASSERT_TRUE(haskey); + } } /* reinsert all edges */ diff --git a/tests/gtests/blenlib/BLI_index_mask_test.cc b/source/blender/blenlib/tests/BLI_index_mask_test.cc index bce467cadd9..4d6060e51c9 100644 --- a/tests/gtests/blenlib/BLI_index_mask_test.cc +++ b/source/blender/blenlib/tests/BLI_index_mask_test.cc @@ -3,7 +3,7 @@ #include "BLI_index_mask.hh" #include "testing/testing.h" -namespace blender { +namespace blender::tests { TEST(index_mask, DefaultConstructor) { @@ -40,4 +40,4 @@ TEST(index_mask, RangeConstructor) EXPECT_EQ(indices[2], 5); } -} // namespace blender +} // namespace blender::tests diff --git a/tests/gtests/blenlib/BLI_index_range_test.cc b/source/blender/blenlib/tests/BLI_index_range_test.cc index 1c4d4a1a1f3..d472ded0f18 100644 --- a/tests/gtests/blenlib/BLI_index_range_test.cc +++ b/source/blender/blenlib/tests/BLI_index_range_test.cc @@ -5,7 +5,7 @@ #include "BLI_vector.hh" #include "testing/testing.h" -namespace blender { +namespace blender::tests { TEST(index_range, DefaultConstructor) { @@ -140,4 +140,4 @@ TEST(index_range, AsSpan) EXPECT_EQ(span[3], 7); } -} // namespace blender +} // namespace blender::tests diff --git a/tests/gtests/blenlib/BLI_linear_allocator_test.cc b/source/blender/blenlib/tests/BLI_linear_allocator_test.cc index 9c2dcdc7457..44b70d1f55d 100644 --- a/tests/gtests/blenlib/BLI_linear_allocator_test.cc +++ b/source/blender/blenlib/tests/BLI_linear_allocator_test.cc @@ -4,7 +4,7 @@ #include "BLI_strict_flags.h" #include "testing/testing.h" -namespace blender { +namespace blender::tests { static bool is_aligned(void *ptr, uint alignment) { @@ -115,4 +115,4 @@ TEST(linear_allocator, ConstructArrayCopy) EXPECT_EQ(span2[2], 3); } -} // namespace blender +} // namespace blender::tests diff --git a/tests/gtests/blenlib/BLI_map_test.cc b/source/blender/blenlib/tests/BLI_map_test.cc index cebce218112..fe7b0f01279 100644 --- a/tests/gtests/blenlib/BLI_map_test.cc +++ b/source/blender/blenlib/tests/BLI_map_test.cc @@ -8,7 +8,7 @@ #include "BLI_vector.hh" #include "testing/testing.h" -namespace blender { +namespace blender::tests { TEST(map, DefaultConstructor) { @@ -335,7 +335,7 @@ TEST(map, MoveConstructorSmall) EXPECT_EQ(map2.size(), 2); EXPECT_EQ(map2.lookup(1), 2.0f); EXPECT_EQ(map2.lookup(4), 1.0f); - EXPECT_EQ(map1.size(), 0); + EXPECT_EQ(map1.size(), 0); /* NOLINT: bugprone-use-after-move */ EXPECT_EQ(map1.lookup_ptr(4), nullptr); } @@ -349,7 +349,7 @@ TEST(map, MoveConstructorLarge) EXPECT_EQ(map2.size(), 100); EXPECT_EQ(map2.lookup(1), 1); EXPECT_EQ(map2.lookup(4), 4); - EXPECT_EQ(map1.size(), 0); + EXPECT_EQ(map1.size(), 0); /* NOLINT: bugprone-use-after-move */ EXPECT_EQ(map1.lookup_ptr(4), nullptr); } @@ -363,7 +363,7 @@ TEST(map, MoveAssignment) EXPECT_EQ(map2.size(), 2); EXPECT_EQ(map2.lookup(1), 2.0f); EXPECT_EQ(map2.lookup(4), 1.0f); - EXPECT_EQ(map1.size(), 0); + EXPECT_EQ(map1.size(), 0); /* NOLINT: bugprone-use-after-move */ EXPECT_EQ(map1.lookup_ptr(4), nullptr); } @@ -587,4 +587,4 @@ TEST(map, Benchmark) #endif /* Benchmark */ -} // namespace blender +} // namespace blender::tests diff --git a/tests/gtests/blenlib/BLI_math_base_safe_test.cc b/source/blender/blenlib/tests/BLI_math_base_safe_test.cc index 2e3e083cf92..2e3e083cf92 100644 --- a/tests/gtests/blenlib/BLI_math_base_safe_test.cc +++ b/source/blender/blenlib/tests/BLI_math_base_safe_test.cc diff --git a/tests/gtests/blenlib/BLI_memory_utils_test.cc b/source/blender/blenlib/tests/BLI_memory_utils_test.cc index b99db5c5eca..f3cb02b63d7 100644 --- a/tests/gtests/blenlib/BLI_memory_utils_test.cc +++ b/source/blender/blenlib/tests/BLI_memory_utils_test.cc @@ -5,7 +5,7 @@ #include "BLI_strict_flags.h" #include "testing/testing.h" -namespace blender { +namespace blender::tests { struct MyValue { static inline int alive = 0; @@ -19,7 +19,7 @@ struct MyValue { alive++; } - MyValue(const MyValue &other) + MyValue(const MyValue &UNUSED(other)) { if (alive == 15) { throw std::exception(); @@ -156,4 +156,4 @@ static_assert(is_convertible_pointer_v<int **, int **const>); static_assert(is_convertible_pointer_v<int **, int *const *>); static_assert(is_convertible_pointer_v<int **, int const *const *>); -} // namespace blender +} // namespace blender::tests diff --git a/source/blender/blenlib/tests/BLI_multi_value_map_test.cc b/source/blender/blenlib/tests/BLI_multi_value_map_test.cc new file mode 100644 index 00000000000..7501fbe0d87 --- /dev/null +++ b/source/blender/blenlib/tests/BLI_multi_value_map_test.cc @@ -0,0 +1,109 @@ +/* Apache License, Version 2.0 */ + +#include "BLI_multi_value_map.hh" +#include "BLI_vector.hh" +#include "testing/testing.h" + +namespace blender::tests { + +TEST(multi_value_map, LookupNotExistant) +{ + MultiValueMap<int, int> map; + EXPECT_EQ(map.lookup(5).size(), 0); + map.add(2, 5); + EXPECT_EQ(map.lookup(5).size(), 0); +} + +TEST(multi_value_map, LookupExistant) +{ + MultiValueMap<int, int> map; + map.add(2, 4); + map.add(2, 5); + map.add(3, 6); + + EXPECT_EQ(map.lookup(2).size(), 2); + EXPECT_EQ(map.lookup(2)[0], 4); + EXPECT_EQ(map.lookup(2)[1], 5); + + EXPECT_EQ(map.lookup(3).size(), 1); + EXPECT_EQ(map.lookup(3)[0], 6); +} + +TEST(multi_value_map, AddMultiple) +{ + MultiValueMap<int, int> map; + map.add_multiple(2, {4, 5, 6}); + map.add_multiple(2, {1, 2}); + map.add_multiple(5, {7, 5, 3}); + + EXPECT_EQ(map.lookup(2).size(), 5); + EXPECT_EQ(map.lookup(2)[0], 4); + EXPECT_EQ(map.lookup(2)[1], 5); + EXPECT_EQ(map.lookup(2)[2], 6); + EXPECT_EQ(map.lookup(2)[3], 1); + EXPECT_EQ(map.lookup(2)[4], 2); + + EXPECT_EQ(map.lookup(5).size(), 3); + EXPECT_EQ(map.lookup(5)[0], 7); + EXPECT_EQ(map.lookup(5)[1], 5); + EXPECT_EQ(map.lookup(5)[2], 3); +} + +TEST(multi_value_map, Keys) +{ + MultiValueMap<int, int> map; + map.add(5, 7); + map.add(5, 7); + map.add_multiple(2, {6, 7, 8}); + + Vector<int> keys; + for (int key : map.keys()) { + keys.append(key); + } + + EXPECT_EQ(keys.size(), 2); + EXPECT_TRUE(keys.contains(5)); + EXPECT_TRUE(keys.contains(2)); +} + +TEST(multi_value_map, Values) +{ + MultiValueMap<int, int> map; + map.add(3, 5); + map.add_multiple(3, {1, 2}); + map.add(6, 1); + + Vector<Span<int>> values; + for (Span<int> value_span : map.values()) { + values.append(value_span); + } + + EXPECT_EQ(values.size(), 2); +} + +TEST(multi_value_map, Items) +{ + MultiValueMap<int, int> map; + map.add_multiple(4, {1, 2, 3}); + + for (auto &&item : map.items()) { + int key = item.key; + Span<int> values = item.value; + EXPECT_EQ(key, 4); + EXPECT_EQ(values.size(), 3); + EXPECT_EQ(values[0], 1); + EXPECT_EQ(values[1], 2); + EXPECT_EQ(values[2], 3); + } +} + +TEST(multi_value_map, UniquePtr) +{ + /* Mostly testing if it compiles here. */ + MultiValueMap<std::unique_ptr<int>, std::unique_ptr<int>> map; + map.add(std::make_unique<int>(4), std::make_unique<int>(6)); + map.add(std::make_unique<int>(4), std::make_unique<int>(7)); + EXPECT_EQ(map.lookup(std::make_unique<int>(10)).size(), 0); +} + +} // namespace blender::tests diff --git a/tests/gtests/blenlib/BLI_set_test.cc b/source/blender/blenlib/tests/BLI_set_test.cc index fcd958dc2f1..7bd0b258df8 100644 --- a/tests/gtests/blenlib/BLI_set_test.cc +++ b/source/blender/blenlib/tests/BLI_set_test.cc @@ -12,6 +12,7 @@ #include "testing/testing.h" namespace blender { +namespace tests { TEST(set, DefaultConstructor) { @@ -81,7 +82,7 @@ TEST(set, MoveConstructor) Set<int> set = {1, 2, 3}; EXPECT_EQ(set.size(), 3); Set<int> set2(std::move(set)); - EXPECT_EQ(set.size(), 0); + EXPECT_EQ(set.size(), 0); /* NOLINT: bugprone-use-after-move */ EXPECT_EQ(set2.size(), 3); } @@ -106,7 +107,7 @@ TEST(set, MoveAssignment) EXPECT_EQ(set.size(), 3); Set<int> set2; set2 = std::move(set); - EXPECT_EQ(set.size(), 0); + EXPECT_EQ(set.size(), 0); /* NOLINT: bugprone-use-after-move */ EXPECT_EQ(set2.size(), 3); } @@ -279,31 +280,32 @@ struct Type2 { uint32_t value; }; -bool operator==(const Type1 &a, const Type1 &b) +static bool operator==(const Type1 &a, const Type1 &b) { return a.value == b.value; } -bool operator==(const Type1 &a, const Type2 &b) -{ - return a.value == b.value; -} -bool operator==(const Type2 &a, const Type1 &b) +static bool operator==(const Type2 &a, const Type1 &b) { return a.value == b.value; } -template<> struct DefaultHash<Type1> { - uint32_t operator()(const Type1 &value) const +} // namespace tests + +/* This has to be defined in ::blender namespace. */ +template<> struct DefaultHash<tests::Type1> { + uint32_t operator()(const tests::Type1 &value) const { return value.value; } - uint32_t operator()(const Type2 &value) const + uint32_t operator()(const tests::Type2 &value) const { return value.value; } }; +namespace tests { + TEST(set, ContainsAs) { Set<Type1> set; @@ -559,4 +561,5 @@ TEST(set, Benchmark) #endif /* Benchmark */ +} // namespace tests } // namespace blender diff --git a/tests/gtests/blenlib/BLI_span_test.cc b/source/blender/blenlib/tests/BLI_span_test.cc index 9c2e7cf26fb..587497624f4 100644 --- a/tests/gtests/blenlib/BLI_span_test.cc +++ b/source/blender/blenlib/tests/BLI_span_test.cc @@ -5,7 +5,7 @@ #include "BLI_vector.hh" #include "testing/testing.h" -namespace blender { +namespace blender::tests { TEST(span, FromSmallVector) { @@ -295,4 +295,17 @@ TEST(span, VoidPointerSpan) func1({&a, &b, &c}); } -} // namespace blender +TEST(span, CopyFrom) +{ + std::array<int, 4> src = {5, 6, 7, 8}; + std::array<int, 4> dst = {1, 2, 3, 4}; + + EXPECT_EQ(dst[2], 3); + MutableSpan(dst).copy_from(src); + EXPECT_EQ(dst[0], 5); + EXPECT_EQ(dst[1], 6); + EXPECT_EQ(dst[2], 7); + EXPECT_EQ(dst[3], 8); +} + +} // namespace blender::tests diff --git a/tests/gtests/blenlib/BLI_stack_cxx_test.cc b/source/blender/blenlib/tests/BLI_stack_cxx_test.cc index 43b3dd8b3ae..3572e751b88 100644 --- a/tests/gtests/blenlib/BLI_stack_cxx_test.cc +++ b/source/blender/blenlib/tests/BLI_stack_cxx_test.cc @@ -5,7 +5,7 @@ #include "BLI_vector.hh" #include "testing/testing.h" -namespace blender { +namespace blender::tests { TEST(stack, DefaultConstructor) { @@ -45,7 +45,7 @@ TEST(stack, MoveConstructor) { Stack<int> stack1 = {1, 2, 3, 4, 5, 6, 7}; Stack<int> stack2 = std::move(stack1); - EXPECT_EQ(stack1.size(), 0); + EXPECT_EQ(stack1.size(), 0); /* NOLINT: bugprone-use-after-move */ EXPECT_EQ(stack2.size(), 7); for (int i = 7; i >= 1; i--) { EXPECT_EQ(stack2.pop(), i); @@ -75,7 +75,7 @@ TEST(stack, MoveAssignment) Stack<int> stack1 = {1, 2, 3, 4, 5, 6, 7}; Stack<int> stack2 = {5, 3, 7, 2, 2}; stack2 = std::move(stack1); - EXPECT_EQ(stack1.size(), 0); + EXPECT_EQ(stack1.size(), 0); /* NOLINT: bugprone-use-after-move */ EXPECT_EQ(stack2.size(), 7); for (int i = 7; i >= 1; i--) { EXPECT_EQ(stack2.pop(), i); @@ -185,4 +185,4 @@ TEST(stack, OveralignedValues) } } -} // namespace blender +} // namespace blender::tests diff --git a/tests/gtests/blenlib/BLI_string_ref_test.cc b/source/blender/blenlib/tests/BLI_string_ref_test.cc index 83099741c29..d08c8a77455 100644 --- a/tests/gtests/blenlib/BLI_string_ref_test.cc +++ b/source/blender/blenlib/tests/BLI_string_ref_test.cc @@ -5,7 +5,7 @@ #include "BLI_vector.hh" #include "testing/testing.h" -namespace blender { +namespace blender::tests { TEST(string_ref_null, DefaultConstructor) { @@ -274,4 +274,4 @@ TEST(string_ref, Copy) EXPECT_EQ(ref, dst); } -} // namespace blender +} // namespace blender::tests diff --git a/tests/gtests/blenlib/BLI_vector_set_test.cc b/source/blender/blenlib/tests/BLI_vector_set_test.cc index 116c4747c5a..8f3db8d8403 100644 --- a/tests/gtests/blenlib/BLI_vector_set_test.cc +++ b/source/blender/blenlib/tests/BLI_vector_set_test.cc @@ -4,7 +4,7 @@ #include "BLI_vector_set.hh" #include "testing/testing.h" -namespace blender { +namespace blender::tests { TEST(vector_set, DefaultConstructor) { @@ -57,7 +57,7 @@ TEST(vector_set, Move) { VectorSet<int> set1 = {1, 2, 3}; VectorSet<int> set2 = std::move(set1); - EXPECT_EQ(set1.size(), 0); + EXPECT_EQ(set1.size(), 0); /* NOLINT: bugprone-use-after-move */ EXPECT_EQ(set2.size(), 3); } @@ -66,7 +66,7 @@ TEST(vector_set, MoveAssignment) VectorSet<int> set1 = {1, 2, 3}; VectorSet<int> set2 = {}; set2 = std::move(set1); - EXPECT_EQ(set1.size(), 0); + EXPECT_EQ(set1.size(), 0); /* NOLINT: bugprone-use-after-move */ EXPECT_EQ(set2.size(), 3); } @@ -161,4 +161,4 @@ TEST(vector_set, Remove) EXPECT_FALSE(set.contains(5)); } -} // namespace blender +} // namespace blender::tests diff --git a/tests/gtests/blenlib/BLI_vector_test.cc b/source/blender/blenlib/tests/BLI_vector_test.cc index f581626d1ad..f72dfc5deb8 100644 --- a/tests/gtests/blenlib/BLI_vector_test.cc +++ b/source/blender/blenlib/tests/BLI_vector_test.cc @@ -5,7 +5,7 @@ #include "testing/testing.h" #include <forward_list> -namespace blender { +namespace blender::tests { TEST(vector, DefaultConstructor) { @@ -167,7 +167,7 @@ TEST(vector, MoveConstructor) Vector<int> vec1 = {1, 2, 3, 4}; Vector<int> vec2(std::move(vec1)); - EXPECT_EQ(vec1.size(), 0); + EXPECT_EQ(vec1.size(), 0); /* NOLINT: bugprone-use-after-move */ EXPECT_EQ(vec2.size(), 4); EXPECT_EQ(vec2[0], 1); EXPECT_EQ(vec2[1], 2); @@ -180,7 +180,7 @@ TEST(vector, MoveConstructor2) Vector<int, 2> vec1 = {1, 2, 3, 4}; Vector<int, 3> vec2(std::move(vec1)); - EXPECT_EQ(vec1.size(), 0); + EXPECT_EQ(vec1.size(), 0); /* NOLINT: bugprone-use-after-move */ EXPECT_EQ(vec2.size(), 4); EXPECT_EQ(vec2[0], 1); EXPECT_EQ(vec2[1], 2); @@ -193,7 +193,7 @@ TEST(vector, MoveConstructor3) Vector<int, 20> vec1 = {1, 2, 3, 4}; Vector<int, 1> vec2(std::move(vec1)); - EXPECT_EQ(vec1.size(), 0); + EXPECT_EQ(vec1.size(), 0); /* NOLINT: bugprone-use-after-move */ EXPECT_EQ(vec2.size(), 4); EXPECT_EQ(vec2[2], 3); } @@ -203,7 +203,7 @@ TEST(vector, MoveConstructor4) Vector<int, 5> vec1 = {1, 2, 3, 4}; Vector<int, 6> vec2(std::move(vec1)); - EXPECT_EQ(vec1.size(), 0); + EXPECT_EQ(vec1.size(), 0); /* NOLINT: bugprone-use-after-move */ EXPECT_EQ(vec2.size(), 4); EXPECT_EQ(vec2[3], 4); } @@ -473,11 +473,11 @@ class TypeConstructMock { { } - TypeConstructMock(const TypeConstructMock &other) : copy_constructed(true) + TypeConstructMock(const TypeConstructMock &UNUSED(other)) : copy_constructed(true) { } - TypeConstructMock(TypeConstructMock &&other) noexcept : move_constructed(true) + TypeConstructMock(TypeConstructMock &&UNUSED(other)) noexcept : move_constructed(true) { } @@ -636,4 +636,4 @@ TEST(vector, Fill) EXPECT_EQ(vec[4], 3); } -} // namespace blender +} // namespace blender::tests diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 9320187f2a0..b37029726f4 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -4162,7 +4162,7 @@ static void direct_link_curve(BlendDataReader *reader, Curve *cu) direct_link_animdata(reader, cu->adt); /* Protect against integer overflow vulnerability. */ - CLAMP(cu->len_wchar, 0, INT_MAX - 4); + CLAMP(cu->len_char32, 0, INT_MAX - 4); BLO_read_pointer_array(reader, (void **)&cu->mat); @@ -8677,9 +8677,8 @@ static void direct_link_volume(BlendDataReader *reader, Volume *volume) static void lib_link_simulation(BlendLibReader *reader, Simulation *simulation) { - LISTBASE_FOREACH ( - PersistentDataHandleItem *, handle_item, &simulation->persistent_data_handles) { - BLO_read_id_address(reader, simulation->id.lib, &handle_item->id); + LISTBASE_FOREACH (SimulationDependency *, dependency, &simulation->dependencies) { + BLO_read_id_address(reader, simulation->id.lib, &dependency->id); } } @@ -8698,7 +8697,7 @@ static void direct_link_simulation(BlendDataReader *reader, Simulation *simulati } } - BLO_read_list(reader, &simulation->persistent_data_handles); + BLO_read_list(reader, &simulation->dependencies); } /** \} */ @@ -11107,9 +11106,8 @@ static void expand_simulation(BlendExpander *expander, Simulation *simulation) if (simulation->adt) { expand_animdata(expander, simulation->adt); } - LISTBASE_FOREACH ( - PersistentDataHandleItem *, handle_item, &simulation->persistent_data_handles) { - BLO_expand(expander, handle_item->id); + LISTBASE_FOREACH (SimulationDependency *, dependency, &simulation->dependencies) { + BLO_expand(expander, dependency->id); } } diff --git a/source/blender/blenloader/intern/versioning_260.c b/source/blender/blenloader/intern/versioning_260.c index b3bf8991c3e..5e91fea3e20 100644 --- a/source/blender/blenloader/intern/versioning_260.c +++ b/source/blender/blenloader/intern/versioning_260.c @@ -2520,7 +2520,7 @@ void blo_do_versions_260(FileData *fd, Library *UNUSED(lib), Main *bmain) for (cu = bmain->curves.first; cu; cu = cu->id.next) { if (cu->str) { - cu->len_wchar = BLI_strlen_utf8(cu->str); + cu->len_char32 = BLI_strlen_utf8(cu->str); } } } diff --git a/source/blender/blenloader/intern/versioning_userdef.c b/source/blender/blenloader/intern/versioning_userdef.c index 50e3b375166..e2dc27d7e88 100644 --- a/source/blender/blenloader/intern/versioning_userdef.c +++ b/source/blender/blenloader/intern/versioning_userdef.c @@ -366,7 +366,7 @@ void BLO_version_defaults_userpref_blend(Main *bmain, UserDef *userdef) } if (!USER_VERSION_ATLEAST(250, 0)) { /* adjust grease-pencil distances */ - userdef->gp_manhattendist = 1; + userdef->gp_manhattandist = 1; userdef->gp_euclideandist = 2; /* adjust default interpolation for new IPO-curves */ diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index 46ac6b43c92..3db2fada85f 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -2007,7 +2007,7 @@ static void write_curve(BlendWriter *writer, Curve *cu, const void *id_address) if (cu->vfont) { BLO_write_raw(writer, cu->len + 1, cu->str); - BLO_write_struct_array(writer, CharInfo, cu->len_wchar + 1, cu->strinfo); + BLO_write_struct_array(writer, CharInfo, cu->len_char32 + 1, cu->strinfo); BLO_write_struct_array(writer, TextBox, cu->totbox, cu->tb); } else { @@ -3862,7 +3862,7 @@ static void write_simulation(BlendWriter *writer, Simulation *simulation, const } } - BLO_write_struct_list(writer, PersistentDataHandleItem, &simulation->persistent_data_handles); + BLO_write_struct_list(writer, SimulationDependency, &simulation->dependencies); } } diff --git a/source/blender/bmesh/tools/bmesh_bevel.c b/source/blender/bmesh/tools/bmesh_bevel.c index 588f716142d..39174a49283 100644 --- a/source/blender/bmesh/tools/bmesh_bevel.c +++ b/source/blender/bmesh/tools/bmesh_bevel.c @@ -917,7 +917,7 @@ static void math_layer_info_init(BevelParams *bp, BMesh *bm) * segment (and its continuation into vmesh) can usually arbitrarily be * the previous face or the next face. * Or, for the center polygon of a corner, all of the faces around - * the vertex are possible choices. + * the vertex are possibleface_component choices. * If we just choose randomly, the resulting UV maps or material * assignment can look ugly/inconsistent. * Allow for the case when arguments are null. diff --git a/source/blender/compositor/operations/COM_InpaintOperation.cpp b/source/blender/compositor/operations/COM_InpaintOperation.cpp index 0967984899d..b8329b1cb29 100644 --- a/source/blender/compositor/operations/COM_InpaintOperation.cpp +++ b/source/blender/compositor/operations/COM_InpaintOperation.cpp @@ -34,7 +34,7 @@ InpaintSimpleOperation::InpaintSimpleOperation() : NodeOperation() this->setComplex(true); this->m_inputImageProgram = NULL; this->m_pixelorder = NULL; - this->m_manhatten_distance = NULL; + this->m_manhattan_distance = NULL; this->m_cached_buffer = NULL; this->m_cached_buffer_ready = false; } @@ -43,7 +43,7 @@ void InpaintSimpleOperation::initExecution() this->m_inputImageProgram = this->getInputSocketReader(0); this->m_pixelorder = NULL; - this->m_manhatten_distance = NULL; + this->m_manhattan_distance = NULL; this->m_cached_buffer = NULL; this->m_cached_buffer_ready = false; @@ -85,7 +85,7 @@ int InpaintSimpleOperation::mdist(int x, int y) ASSERT_XY_RANGE(x, y); - return this->m_manhatten_distance[y * width + x]; + return this->m_manhattan_distance[y * width + x]; } bool InpaintSimpleOperation::next_pixel(int &x, int &y, int &curr, int iters) @@ -108,11 +108,11 @@ bool InpaintSimpleOperation::next_pixel(int &x, int &y, int &curr, int iters) return true; } -void InpaintSimpleOperation::calc_manhatten_distance() +void InpaintSimpleOperation::calc_manhattan_distance() { int width = this->getWidth(); int height = this->getHeight(); - short *m = this->m_manhatten_distance = (short *)MEM_mallocN(sizeof(short) * width * height, + short *m = this->m_manhattan_distance = (short *)MEM_mallocN(sizeof(short) * width * height, __func__); int *offsets; @@ -223,7 +223,7 @@ void *InpaintSimpleOperation::initializeTileData(rcti *rect) MemoryBuffer *buf = (MemoryBuffer *)this->m_inputImageProgram->initializeTileData(rect); this->m_cached_buffer = (float *)MEM_dupallocN(buf->getBuffer()); - this->calc_manhatten_distance(); + this->calc_manhattan_distance(); int curr = 0; int x, y; @@ -258,9 +258,9 @@ void InpaintSimpleOperation::deinitExecution() this->m_pixelorder = NULL; } - if (this->m_manhatten_distance) { - MEM_freeN(this->m_manhatten_distance); - this->m_manhatten_distance = NULL; + if (this->m_manhattan_distance) { + MEM_freeN(this->m_manhattan_distance); + this->m_manhattan_distance = NULL; } this->m_cached_buffer_ready = false; } diff --git a/source/blender/compositor/operations/COM_InpaintOperation.h b/source/blender/compositor/operations/COM_InpaintOperation.h index 2fef7c590ea..76c1ae81b28 100644 --- a/source/blender/compositor/operations/COM_InpaintOperation.h +++ b/source/blender/compositor/operations/COM_InpaintOperation.h @@ -34,7 +34,7 @@ class InpaintSimpleOperation : public NodeOperation { int *m_pixelorder; int m_area_size; - short *m_manhatten_distance; + short *m_manhattan_distance; public: InpaintSimpleOperation(); @@ -65,7 +65,7 @@ class InpaintSimpleOperation : public NodeOperation { rcti *output); private: - void calc_manhatten_distance(); + void calc_manhattan_distance(); void clamp_xy(int &x, int &y); float *get_pixel(int x, int y); int mdist(int x, int y); diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc index c8309656f21..57bc673a521 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc @@ -1466,6 +1466,18 @@ void DepsgraphNodeBuilder::build_light(Light *lamp) function_bind(BKE_light_eval, _1, lamp_cow)); } +void DepsgraphNodeBuilder::build_nodetree_socket(bNodeSocket *socket) +{ + build_idproperties(socket->prop); + + if (socket->type == SOCK_OBJECT) { + build_id((ID *)((bNodeSocketValueObject *)socket->default_value)->value); + } + else if (socket->type == SOCK_IMAGE) { + build_id((ID *)((bNodeSocketValueImage *)socket->default_value)->value); + } +} + void DepsgraphNodeBuilder::build_nodetree(bNodeTree *ntree) { if (ntree == nullptr) { @@ -1494,10 +1506,10 @@ void DepsgraphNodeBuilder::build_nodetree(bNodeTree *ntree) LISTBASE_FOREACH (bNode *, bnode, &ntree->nodes) { build_idproperties(bnode->prop); LISTBASE_FOREACH (bNodeSocket *, socket, &bnode->inputs) { - build_idproperties(socket->prop); + build_nodetree_socket(socket); } LISTBASE_FOREACH (bNodeSocket *, socket, &bnode->outputs) { - build_idproperties(socket->prop); + build_nodetree_socket(socket); } ID *id = bnode->id; @@ -1780,8 +1792,10 @@ void DepsgraphNodeBuilder::build_simulation(Simulation *simulation) return; } add_id_node(&simulation->id); + build_idproperties(simulation->id.properties); build_animdata(&simulation->id); build_parameters(&simulation->id); + build_nodetree(simulation->nodetree); Simulation *simulation_cow = get_cow_datablock(simulation); Scene *scene_cow = get_cow_datablock(scene_); diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h index 256fa3450a6..40f42705a52 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h @@ -31,6 +31,7 @@ #include "DEG_depsgraph.h" +struct bNodeSocket; struct CacheFile; struct Camera; struct Collection; @@ -211,6 +212,7 @@ class DepsgraphNodeBuilder : public DepsgraphBuilder { virtual void build_camera(Camera *camera); virtual void build_light(Light *lamp); virtual void build_nodetree(bNodeTree *ntree); + virtual void build_nodetree_socket(bNodeSocket *socket); virtual void build_material(Material *ma); virtual void build_materials(Material **materials, int num_materials); virtual void build_freestyle_lineset(FreestyleLineSet *fls); diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc index c5c509ee853..d0e8246caa9 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc @@ -2276,6 +2276,24 @@ void DepsgraphRelationBuilder::build_light(Light *lamp) add_relation(lamp_parameters_key, shading_key, "Light Shading Parameters"); } +void DepsgraphRelationBuilder::build_nodetree_socket(bNodeSocket *socket) +{ + build_idproperties(socket->prop); + + if (socket->type == SOCK_OBJECT) { + Object *object = ((bNodeSocketValueObject *)socket->default_value)->value; + if (object != nullptr) { + build_object(object); + } + } + else if (socket->type == SOCK_IMAGE) { + Image *image = ((bNodeSocketValueImage *)socket->default_value)->value; + if (image != nullptr) { + build_image(image); + } + } +} + void DepsgraphRelationBuilder::build_nodetree(bNodeTree *ntree) { if (ntree == nullptr) { @@ -2292,10 +2310,10 @@ void DepsgraphRelationBuilder::build_nodetree(bNodeTree *ntree) LISTBASE_FOREACH (bNode *, bnode, &ntree->nodes) { build_idproperties(bnode->prop); LISTBASE_FOREACH (bNodeSocket *, socket, &bnode->inputs) { - build_idproperties(socket->prop); + build_nodetree_socket(socket); } LISTBASE_FOREACH (bNodeSocket *, socket, &bnode->outputs) { - build_idproperties(socket->prop); + build_nodetree_socket(socket); } ID *id = bnode->id; @@ -2602,13 +2620,39 @@ void DepsgraphRelationBuilder::build_simulation(Simulation *simulation) if (built_map_.checkIsBuiltAndTag(simulation)) { return; } + build_idproperties(simulation->id.properties); build_animdata(&simulation->id); build_parameters(&simulation->id); - OperationKey simulation_update_key( + build_nodetree(simulation->nodetree); + build_nested_nodetree(&simulation->id, simulation->nodetree); + + OperationKey simulation_eval_key( &simulation->id, NodeType::SIMULATION, OperationCode::SIMULATION_EVAL); TimeSourceKey time_src_key; - add_relation(time_src_key, simulation_update_key, "TimeSrc -> Simulation"); + add_relation(time_src_key, simulation_eval_key, "TimeSrc -> Simulation"); + + OperationKey nodetree_key( + &simulation->nodetree->id, NodeType::PARAMETERS, OperationCode::PARAMETERS_EXIT); + add_relation(nodetree_key, simulation_eval_key, "NodeTree -> Simulation", 0); + + LISTBASE_FOREACH (SimulationDependency *, dependency, &simulation->dependencies) { + if (dependency->id == nullptr) { + continue; + } + build_id(dependency->id); + if (GS(dependency->id->name) == ID_OB) { + Object *object = (Object *)dependency->id; + if (dependency->flag & SIM_DEPENDS_ON_TRANSFORM) { + ComponentKey object_transform_key(&object->id, NodeType::TRANSFORM); + add_relation(object_transform_key, simulation_eval_key, "Object Transform -> Simulation"); + } + if (dependency->flag & SIM_DEPENDS_ON_GEOMETRY) { + ComponentKey object_geometry_key(&object->id, NodeType::GEOMETRY); + add_relation(object_geometry_key, simulation_eval_key, "Object Geometry -> Simulation"); + } + } + } } void DepsgraphRelationBuilder::build_scene_sequencer(Scene *scene) diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.h b/source/blender/depsgraph/intern/builder/deg_builder_relations.h index b4b0dc71f85..04f2a3f911d 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.h @@ -46,6 +46,7 @@ #include "intern/node/deg_node_operation.h" struct Base; +struct bNodeSocket; struct CacheFile; struct Camera; struct Collection; @@ -275,6 +276,7 @@ class DepsgraphRelationBuilder : public DepsgraphBuilder { virtual void build_camera(Camera *camera); virtual void build_light(Light *lamp); virtual void build_nodetree(bNodeTree *ntree); + virtual void build_nodetree_socket(bNodeSocket *socket); virtual void build_material(Material *ma); virtual void build_materials(Material **materials, int num_materials); virtual void build_freestyle_lineset(FreestyleLineSet *fls); diff --git a/source/blender/depsgraph/intern/builder/deg_builder_rna.h b/source/blender/depsgraph/intern/builder/deg_builder_rna.h index c48c6489c47..d03903d508c 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_rna.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_rna.h @@ -96,7 +96,7 @@ class RNANodeQuery { /* Check whether prop_identifier contains rna_path_component. * - * This checks more than a substring: + * This checks more than a sub-string: * * prop_identifier contains(prop_identifier, "location") * ------------------------ ------------------------------------- diff --git a/source/blender/depsgraph/intern/depsgraph_tag.cc b/source/blender/depsgraph/intern/depsgraph_tag.cc index 848275eb899..1863a333930 100644 --- a/source/blender/depsgraph/intern/depsgraph_tag.cc +++ b/source/blender/depsgraph/intern/depsgraph_tag.cc @@ -786,6 +786,7 @@ void DEG_graph_id_type_tag(Depsgraph *depsgraph, short id_type) DEG_graph_id_type_tag(depsgraph, ID_LA); DEG_graph_id_type_tag(depsgraph, ID_WO); DEG_graph_id_type_tag(depsgraph, ID_SCE); + DEG_graph_id_type_tag(depsgraph, ID_SIM); } const int id_type_index = BKE_idtype_idcode_to_index(id_type); deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(depsgraph); diff --git a/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc index 79d6c8d6a77..82cb311ec45 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc @@ -120,6 +120,7 @@ union NestedIDHackTempStorage { Scene scene; Tex tex; World world; + Simulation simulation; }; /* Set nested owned ID pointers to nullptr. */ @@ -137,6 +138,7 @@ void nested_id_hack_discard_pointers(ID *id_cow) SPECIAL_CASE(ID_MA, Material, nodetree) SPECIAL_CASE(ID_TE, Tex, nodetree) SPECIAL_CASE(ID_WO, World, nodetree) + SPECIAL_CASE(ID_SIM, Simulation, nodetree) SPECIAL_CASE(ID_CU, Curve, key) SPECIAL_CASE(ID_LT, Lattice, key) @@ -185,6 +187,7 @@ const ID *nested_id_hack_get_discarded_pointers(NestedIDHackTempStorage *storage SPECIAL_CASE(ID_MA, Material, nodetree, material) SPECIAL_CASE(ID_TE, Tex, nodetree, tex) SPECIAL_CASE(ID_WO, World, nodetree, world) + SPECIAL_CASE(ID_SIM, Simulation, nodetree, simulation) SPECIAL_CASE(ID_CU, Curve, key, curve) SPECIAL_CASE(ID_LT, Lattice, key, lattice) @@ -224,6 +227,7 @@ void nested_id_hack_restore_pointers(const ID *old_id, ID *new_id) SPECIAL_CASE(ID_SCE, Scene, nodetree) SPECIAL_CASE(ID_TE, Tex, nodetree) SPECIAL_CASE(ID_WO, World, nodetree) + SPECIAL_CASE(ID_SIM, Simulation, nodetree) SPECIAL_CASE(ID_CU, Curve, key) SPECIAL_CASE(ID_LT, Lattice, key) @@ -261,6 +265,7 @@ void ntree_hack_remap_pointers(const Depsgraph *depsgraph, ID *id_cow) SPECIAL_CASE(ID_SCE, Scene, nodetree, bNodeTree) SPECIAL_CASE(ID_TE, Tex, nodetree, bNodeTree) SPECIAL_CASE(ID_WO, World, nodetree, bNodeTree) + SPECIAL_CASE(ID_SIM, Simulation, nodetree, bNodeTree) SPECIAL_CASE(ID_CU, Curve, key, Key) SPECIAL_CASE(ID_LT, Lattice, key, Key) diff --git a/source/blender/draw/engines/gpencil/gpencil_draw_data.c b/source/blender/draw/engines/gpencil/gpencil_draw_data.c index 6e6b35e19ca..d2aa0a45660 100644 --- a/source/blender/draw/engines/gpencil/gpencil_draw_data.c +++ b/source/blender/draw/engines/gpencil/gpencil_draw_data.c @@ -63,7 +63,7 @@ static struct GPUTexture *gpencil_image_texture_get(Image *image, bool *r_alpha_ ibuf = BKE_image_acquire_ibuf(image, &iuser, &lock); if (ibuf != NULL && ibuf->rect != NULL) { - gpu_tex = GPU_texture_from_blender(image, &iuser, ibuf, GL_TEXTURE_2D); + gpu_tex = GPU_texture_from_blender(image, &iuser, ibuf, TEXTARGET_2D); *r_alpha_premult = (image->alpha_mode == IMA_ALPHA_PREMUL); } BKE_image_release_ibuf(image, ibuf, lock); diff --git a/source/blender/draw/engines/overlay/overlay_image.c b/source/blender/draw/engines/overlay/overlay_image.c index 67132a9e0ed..7aad84629e0 100644 --- a/source/blender/draw/engines/overlay/overlay_image.c +++ b/source/blender/draw/engines/overlay/overlay_image.c @@ -175,7 +175,7 @@ static struct GPUTexture *image_camera_background_texture_get(CameraBGImage *bgp } width = ibuf->x; height = ibuf->y; - tex = GPU_texture_from_blender(image, iuser, ibuf, GL_TEXTURE_2D); + tex = GPU_texture_from_blender(image, iuser, ibuf, TEXTARGET_2D); BKE_image_release_ibuf(image, ibuf, lock); iuser->scene = NULL; @@ -203,7 +203,7 @@ static struct GPUTexture *image_camera_background_texture_get(CameraBGImage *bgp } BKE_movieclip_user_set_frame(&bgpic->cuser, ctime); - tex = GPU_texture_from_movieclip(clip, &bgpic->cuser, GL_TEXTURE_2D); + tex = GPU_texture_from_movieclip(clip, &bgpic->cuser, TEXTARGET_2D); if (tex == NULL) { return NULL; } @@ -383,7 +383,7 @@ void OVERLAY_image_empty_cache_populate(OVERLAY_Data *vedata, Object *ob) if (ima != NULL) { ImageUser iuser = *ob->iuser; camera_background_images_stereo_setup(draw_ctx->scene, draw_ctx->v3d, ima, &iuser); - tex = GPU_texture_from_blender(ima, &iuser, NULL, GL_TEXTURE_2D); + tex = GPU_texture_from_blender(ima, &iuser, NULL, TEXTARGET_2D); if (tex) { size[0] = GPU_texture_orig_width(tex); size[1] = GPU_texture_orig_height(tex); diff --git a/source/blender/draw/engines/overlay/overlay_paint.c b/source/blender/draw/engines/overlay/overlay_paint.c index e94cc820568..44eded94e63 100644 --- a/source/blender/draw/engines/overlay/overlay_paint.c +++ b/source/blender/draw/engines/overlay/overlay_paint.c @@ -136,7 +136,7 @@ void OVERLAY_paint_cache_init(OVERLAY_Data *vedata) state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL | DRW_STATE_BLEND_ALPHA; DRW_PASS_CREATE(psl->paint_color_ps, state | pd->clipping_state); - GPUTexture *tex = GPU_texture_from_blender(imapaint->stencil, NULL, NULL, GL_TEXTURE_2D); + GPUTexture *tex = GPU_texture_from_blender(imapaint->stencil, NULL, NULL, TEXTARGET_2D); const bool mask_premult = (imapaint->stencil->alpha_mode == IMA_ALPHA_PREMUL); const bool mask_inverted = (imapaint->flag & IMAGEPAINT_PROJECT_LAYER_STENCIL_INV) != 0; diff --git a/source/blender/draw/engines/workbench/workbench_materials.c b/source/blender/draw/engines/workbench/workbench_materials.c index 3fa2e7ef084..0493b42f9c6 100644 --- a/source/blender/draw/engines/workbench/workbench_materials.c +++ b/source/blender/draw/engines/workbench/workbench_materials.c @@ -261,11 +261,11 @@ DRWShadingGroup *workbench_image_setup_ex(WORKBENCH_PrivateData *wpd, if (ima) { if (ima->source == IMA_SRC_TILED) { - tex = GPU_texture_from_blender(ima, iuser, NULL, GL_TEXTURE_2D_ARRAY); - tex_tile_data = GPU_texture_from_blender(ima, iuser, NULL, GL_TEXTURE_1D_ARRAY); + tex = GPU_texture_from_blender(ima, iuser, NULL, TEXTARGET_2D_ARRAY); + tex_tile_data = GPU_texture_from_blender(ima, iuser, NULL, TEXTARGET_TILE_MAPPING); } else { - tex = GPU_texture_from_blender(ima, iuser, NULL, GL_TEXTURE_2D); + tex = GPU_texture_from_blender(ima, iuser, NULL, TEXTARGET_2D); } } diff --git a/source/blender/draw/intern/draw_cache_impl_pointcloud.c b/source/blender/draw/intern/draw_cache_impl_pointcloud.c index 17902f27513..06cedb9f72c 100644 --- a/source/blender/draw/intern/draw_cache_impl_pointcloud.c +++ b/source/blender/draw/intern/draw_cache_impl_pointcloud.c @@ -158,6 +158,7 @@ static void pointcloud_batch_cache_ensure_pos(Object *ob, PointCloudBatchCache * const bool has_radius = pointcloud->radius != NULL; static GPUVertFormat format = {0}; + static GPUVertFormat format_no_radius = {0}; static uint pos; if (format.attr_len == 0) { /* initialize vertex format */ @@ -167,11 +168,11 @@ static void pointcloud_batch_cache_ensure_pos(Object *ob, PointCloudBatchCache * * If the vertex shader has more components than the array provides, the extras are given * values from the vector (0, 0, 0, 1) for the missing XYZW components. */ - int comp_len = has_radius ? 4 : 3; - pos = GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, comp_len, GPU_FETCH_FLOAT); + pos = GPU_vertformat_attr_add(&format_no_radius, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + pos = GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); } - cache->pos = GPU_vertbuf_create_with_format(&format); + cache->pos = GPU_vertbuf_create_with_format(has_radius ? &format : &format_no_radius); GPU_vertbuf_data_alloc(cache->pos, pointcloud->totpoint); if (has_radius) { diff --git a/source/blender/draw/intern/draw_manager_data.c b/source/blender/draw/intern/draw_manager_data.c index 9d8050504ab..a8105785d53 100644 --- a/source/blender/draw/intern/draw_manager_data.c +++ b/source/blender/draw/intern/draw_manager_data.c @@ -1295,7 +1295,7 @@ static void drw_shgroup_material_texture(DRWShadingGroup *grp, GPUMaterialTexture *tex, const char *name, eGPUSamplerState state, - int textarget) + eGPUTextureTarget textarget) { GPUTexture *gputex = GPU_texture_from_blender(tex->ima, tex->iuser, NULL, textarget); @@ -1316,13 +1316,13 @@ void DRW_shgroup_add_material_resources(DRWShadingGroup *grp, struct GPUMaterial /* Image */ if (tex->tiled_mapping_name[0]) { drw_shgroup_material_texture( - grp, tex, tex->sampler_name, tex->sampler_state, GL_TEXTURE_2D_ARRAY); + grp, tex, tex->sampler_name, tex->sampler_state, TEXTARGET_2D_ARRAY); drw_shgroup_material_texture( - grp, tex, tex->tiled_mapping_name, tex->sampler_state, GL_TEXTURE_1D_ARRAY); + grp, tex, tex->tiled_mapping_name, tex->sampler_state, TEXTARGET_TILE_MAPPING); } else { drw_shgroup_material_texture( - grp, tex, tex->sampler_name, tex->sampler_state, GL_TEXTURE_2D); + grp, tex, tex->sampler_name, tex->sampler_state, TEXTARGET_2D); } } else if (tex->colorband) { diff --git a/source/blender/draw/intern/draw_select_buffer.c b/source/blender/draw/intern/draw_select_buffer.c index 84c8d0f861f..ee5561e1e38 100644 --- a/source/blender/draw/intern/draw_select_buffer.c +++ b/source/blender/draw/intern/draw_select_buffer.c @@ -395,7 +395,7 @@ uint DRW_select_buffer_find_nearest_to_point(struct Depsgraph *depsgraph, int center_x = width / 2; int center_y = height / 2; - /* Manhatten distance in keeping with other screen-based selection. */ + /* Manhattan distance in keeping with other screen-based selection. */ *dist = (uint)(abs(hit_x - center_x) + abs(hit_y - center_y)); /* Indices start at 1 here. */ diff --git a/source/blender/editors/curve/editfont.c b/source/blender/editors/curve/editfont.c index b759277572c..d78c543f94b 100644 --- a/source/blender/editors/curve/editfont.c +++ b/source/blender/editors/curve/editfont.c @@ -76,9 +76,9 @@ static int kill_selection(Object *obedit, int ins); /** \name Internal Utilities * \{ */ -static wchar_t findaccent(wchar_t char1, uint code) +static char32_t findaccent(char32_t char1, uint code) { - wchar_t new = 0; + char32_t new = 0; if (char1 == 'a') { if (code == '`') { @@ -682,7 +682,7 @@ static void txt_add_object(bContext *C, TextLine *firstline, int totline, const cu->strinfo = MEM_callocN((nchars + 4) * sizeof(CharInfo), "strinfo"); cu->len = 0; - cu->len_wchar = nchars - 1; + cu->len_char32 = nchars - 1; cu->pos = 0; s = cu->str; @@ -703,7 +703,7 @@ static void txt_add_object(bContext *C, TextLine *firstline, int totline, const } } - cu->pos = cu->len_wchar; + cu->pos = cu->len_char32; *s = '\0'; WM_event_add_notifier(C, NC_OBJECT | NA_ADDED, obedit); @@ -1661,7 +1661,7 @@ static int insert_text_invoke(bContext *C, wmOperator *op, const wmEvent *event) uintptr_t ascii = event->ascii; int alt = event->alt, shift = event->shift, ctrl = event->ctrl; int event_type = event->type, event_val = event->val; - wchar_t inserted_text[2] = {0}; + char32_t inserted_text[2] = {0}; if (RNA_struct_property_is_set(op->ptr, "text")) { return insert_text_exec(C, op); @@ -1733,7 +1733,7 @@ static int insert_text_invoke(bContext *C, wmOperator *op, const wmEvent *event) /* store as utf8 in RNA string */ char inserted_utf8[8] = {0}; - BLI_strncpy_wchar_as_utf8(inserted_utf8, inserted_text, sizeof(inserted_utf8)); + BLI_str_utf32_as_utf8(inserted_utf8, inserted_text, sizeof(inserted_utf8)); RNA_string_set(op->ptr, "text", inserted_utf8); } @@ -1867,7 +1867,7 @@ void ED_curve_editfont_make(Object *obedit) { Curve *cu = obedit->data; EditFont *ef = cu->editfont; - int len_wchar; + int len_char32; if (ef == NULL) { ef = cu->editfont = MEM_callocN(sizeof(EditFont), "editfont"); @@ -1876,10 +1876,10 @@ void ED_curve_editfont_make(Object *obedit) ef->textbufinfo = MEM_callocN((MAXTEXT + 4) * sizeof(CharInfo), "texteditbufinfo"); } - /* Convert the original text to wchar_t */ - len_wchar = BLI_str_utf8_as_utf32(ef->textbuf, cu->str, MAXTEXT + 4); - BLI_assert(len_wchar == cu->len_wchar); - ef->len = len_wchar; + /* Convert the original text to chat32_t. */ + len_char32 = BLI_str_utf8_as_utf32(ef->textbuf, cu->str, MAXTEXT + 4); + BLI_assert(len_char32 == cu->len_char32); + ef->len = len_char32; BLI_assert(ef->len >= 0); memcpy(ef->textbufinfo, cu->strinfo, ef->len * sizeof(CharInfo)); @@ -1908,7 +1908,7 @@ void ED_curve_editfont_load(Object *obedit) MEM_freeN(cu->str); /* Calculate the actual string length in UTF-8 variable characters */ - cu->len_wchar = ef->len; + cu->len_char32 = ef->len; cu->len = BLI_str_utf32_as_utf8_len(ef->textbuf); /* Alloc memory for UTF-8 variable char length string */ @@ -1920,8 +1920,8 @@ void ED_curve_editfont_load(Object *obedit) if (cu->strinfo) { MEM_freeN(cu->strinfo); } - cu->strinfo = MEM_callocN((cu->len_wchar + 4) * sizeof(CharInfo), "texteditinfo"); - memcpy(cu->strinfo, ef->textbufinfo, cu->len_wchar * sizeof(CharInfo)); + cu->strinfo = MEM_callocN((cu->len_char32 + 4) * sizeof(CharInfo), "texteditinfo"); + memcpy(cu->strinfo, ef->textbufinfo, cu->len_char32 * sizeof(CharInfo)); /* Other vars */ cu->pos = ef->pos; diff --git a/source/blender/editors/gizmo_library/gizmo_draw_utils.c b/source/blender/editors/gizmo_library/gizmo_draw_utils.c index 71a364c60d7..033673a99a8 100644 --- a/source/blender/editors/gizmo_library/gizmo_draw_utils.c +++ b/source/blender/editors/gizmo_library/gizmo_draw_utils.c @@ -29,7 +29,6 @@ #include "ED_view3d.h" #include "GPU_batch.h" -#include "GPU_glew.h" #include "GPU_immediate.h" #include "MEM_guardedalloc.h" diff --git a/source/blender/editors/gpencil/annotate_paint.c b/source/blender/editors/gpencil/annotate_paint.c index 6f700f6c4b8..3aec1db2587 100644 --- a/source/blender/editors/gpencil/annotate_paint.c +++ b/source/blender/editors/gpencil/annotate_paint.c @@ -200,7 +200,7 @@ typedef struct tGPsdata { /* Macros for accessing sensitivity thresholds... */ /* minimum number of pixels mouse should move before new point created */ -#define MIN_MANHATTEN_PX (U.gp_manhattendist) +#define MIN_MANHATTAN_PX (U.gp_manhattandist) /* minimum length of new segment before new point can be added */ #define MIN_EUCLIDEAN_PX (U.gp_euclideandist) @@ -297,7 +297,7 @@ static bool annotation_stroke_filtermval(tGPsdata *p, const float mval[2], float return false; } - if ((dx > MIN_MANHATTEN_PX) && (dy > MIN_MANHATTEN_PX)) { + if ((dx > MIN_MANHATTAN_PX) && (dy > MIN_MANHATTAN_PX)) { return true; } diff --git a/source/blender/editors/gpencil/gpencil_data.c b/source/blender/editors/gpencil/gpencil_data.c index 96b0296a7de..348fb614977 100644 --- a/source/blender/editors/gpencil/gpencil_data.c +++ b/source/blender/editors/gpencil/gpencil_data.c @@ -861,6 +861,154 @@ void GPENCIL_OT_frame_clean_loose(wmOperatorType *ot) INT_MAX); } +/* ********************* Clean Duplicated Frames ************************** */ +static bool gpencil_frame_is_equal(bGPDframe *gpf_a, bGPDframe *gpf_b) +{ + if ((gpf_a == NULL) || (gpf_b == NULL)) { + return false; + } + /* If the number of strokes is different, cannot be equal. */ + int totstrokes_a = BLI_listbase_count(&gpf_a->strokes); + int totstrokes_b = BLI_listbase_count(&gpf_b->strokes); + if ((totstrokes_a == 0) || (totstrokes_b == 0) || (totstrokes_a != totstrokes_b)) { + return false; + } + /* Loop all strokes and check. */ + bGPDstroke *gps_a = gpf_a->strokes.first; + bGPDstroke *gps_b = gpf_b->strokes.first; + for (int i = 0; i < totstrokes_a; i++) { + /* If the number of points is different, cannot be equal. */ + if (gps_a->totpoints != gps_b->totpoints) { + return false; + } + /* Check other variables. */ + if (!equals_v4v4(gps_a->vert_color_fill, gps_b->vert_color_fill)) { + return false; + } + if (gps_a->thickness != gps_b->thickness) { + return false; + } + if (gps_a->mat_nr != gps_b->mat_nr) { + return false; + } + if (gps_a->caps[0] != gps_b->caps[0]) { + return false; + } + if (gps_a->caps[1] != gps_b->caps[1]) { + return false; + } + if (gps_a->hardeness != gps_b->hardeness) { + return false; + } + if (!equals_v2v2(gps_a->aspect_ratio, gps_b->aspect_ratio)) { + return false; + } + if (gps_a->uv_rotation != gps_b->uv_rotation) { + return false; + } + if (!equals_v2v2(gps_a->uv_translation, gps_b->uv_translation)) { + return false; + } + if (gps_a->uv_scale != gps_b->uv_scale) { + return false; + } + + /* Loop points and check if equals or not. */ + for (int p = 0; p < gps_a->totpoints; p++) { + bGPDspoint *pt_a = &gps_a->points[p]; + bGPDspoint *pt_b = &gps_b->points[p]; + if (!equals_v3v3(&pt_a->x, &pt_b->x)) { + return false; + } + if (pt_a->pressure != pt_b->pressure) { + return false; + } + if (pt_a->strength != pt_b->strength) { + return false; + } + if (pt_a->uv_fac != pt_b->uv_fac) { + return false; + } + if (pt_a->uv_rot != pt_b->uv_rot) { + return false; + } + if (!equals_v4v4(pt_a->vert_color, pt_b->vert_color)) { + return false; + } + } + + /* Look at next pair of strokes. */ + gps_a = gps_a->next; + gps_b = gps_b->next; + } + + return true; +} + +static int gpencil_frame_clean_duplicate_exec(bContext *C, wmOperator *op) +{ +#define SELECTED 1 + + bool changed = false; + Object *ob = CTX_data_active_object(C); + bGPdata *gpd = (bGPdata *)ob->data; + const int type = RNA_enum_get(op->ptr, "type"); + + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + /* Only editable and visible layers are considered. */ + if (BKE_gpencil_layer_is_editable(gpl) && (gpl->frames.first != NULL)) { + bGPDframe *gpf = gpl->frames.first; + + if ((type == SELECTED) && ((gpf->flag & GP_FRAME_SELECT) == 0)) { + continue; + } + + while (gpf != NULL) { + if (gpencil_frame_is_equal(gpf, gpf->next)) { + /* Remove frame. */ + BKE_gpencil_layer_frame_delete(gpl, gpf->next); + /* Tag for recalc. */ + changed = true; + } + else { + gpf = gpf->next; + } + } + } + } + + /* notifiers */ + if (changed) { + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + } + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_frame_clean_duplicate(wmOperatorType *ot) +{ + static const EnumPropertyItem clean_type[] = { + {0, "ALL", 0, "All Frames", ""}, + {1, "SELECTED", 0, "Selected Frames", ""}, + {0, NULL, 0, NULL, NULL}, + }; + + /* identifiers */ + ot->name = "Clean Duplicated Frames"; + ot->idname = "GPENCIL_OT_frame_clean_duplicate"; + ot->description = "Remove any duplicated frame"; + + /* callbacks */ + ot->exec = gpencil_frame_clean_duplicate_exec; + ot->poll = gpencil_active_layer_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + ot->prop = RNA_def_enum(ot->srna, "type", clean_type, 0, "Type", ""); +} + /* *********************** Hide Layers ******************************** */ static int gpencil_hide_exec(bContext *C, wmOperator *op) diff --git a/source/blender/editors/gpencil/gpencil_fill.c b/source/blender/editors/gpencil/gpencil_fill.c index 517225e5a81..d7525c3c6a5 100644 --- a/source/blender/editors/gpencil/gpencil_fill.c +++ b/source/blender/editors/gpencil/gpencil_fill.c @@ -416,10 +416,10 @@ static bool gpencil_render_offscreen(tGPDfill *tgpf) /* create a image to see result of template */ if (ibuf->rect_float) { - GPU_offscreen_read_pixels(offscreen, GL_FLOAT, ibuf->rect_float); + GPU_offscreen_read_pixels(offscreen, GPU_DATA_FLOAT, ibuf->rect_float); } else if (ibuf->rect) { - GPU_offscreen_read_pixels(offscreen, GL_UNSIGNED_BYTE, ibuf->rect); + GPU_offscreen_read_pixels(offscreen, GPU_DATA_UNSIGNED_BYTE, ibuf->rect); } if (ibuf->rect_float && ibuf->rect) { IMB_rect_from_float(ibuf); diff --git a/source/blender/editors/gpencil/gpencil_intern.h b/source/blender/editors/gpencil/gpencil_intern.h index bd697dbc768..68424839ab0 100644 --- a/source/blender/editors/gpencil/gpencil_intern.h +++ b/source/blender/editors/gpencil/gpencil_intern.h @@ -485,6 +485,7 @@ void GPENCIL_OT_active_frames_delete_all(struct wmOperatorType *ot); void GPENCIL_OT_frame_duplicate(struct wmOperatorType *ot); void GPENCIL_OT_frame_clean_fill(struct wmOperatorType *ot); void GPENCIL_OT_frame_clean_loose(struct wmOperatorType *ot); +void GPENCIL_OT_frame_clean_duplicate(struct wmOperatorType *ot); void GPENCIL_OT_convert(struct wmOperatorType *ot); void GPENCIL_OT_bake_mesh_animation(struct wmOperatorType *ot); diff --git a/source/blender/editors/gpencil/gpencil_interpolate.c b/source/blender/editors/gpencil/gpencil_interpolate.c index 179f621205b..2d60d84bc19 100644 --- a/source/blender/editors/gpencil/gpencil_interpolate.c +++ b/source/blender/editors/gpencil/gpencil_interpolate.c @@ -136,15 +136,17 @@ static void gpencil_interpolate_free_temp_strokes(bGPDframe *gpf) } /* Helper: Untag all strokes. */ -static void gpencil_interpolate_untag_strokes(bGPDframe *gpf) +static void gpencil_interpolate_untag_strokes(bGPDlayer *gpl) { - if (gpf == NULL) { + if (gpl == NULL) { return; } - LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { - if (gps->flag & GP_STROKE_TAG) { - gps->flag &= ~GP_STROKE_TAG; + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { + if (gps->flag & GP_STROKE_TAG) { + gps->flag &= ~GP_STROKE_TAG; + } } } } @@ -263,15 +265,6 @@ static void gpencil_interpolate_set_points(bContext *C, tGPDinterpolate *tgpi) /* set layers */ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { tGPDinterpolate_layer *tgpil; - - /* Untag strokes to be sure nothing is pending. This must be done for - * all layer because it could be anything tagged and it would be removed - * at the end of the process when all tagged strokes are removed. */ - if (gpl->actframe != NULL) { - gpencil_interpolate_untag_strokes(gpl->actframe); - gpencil_interpolate_untag_strokes(gpl->actframe->next); - } - /* all layers or only active */ if (!(tgpi->flag & GP_TOOLFLAG_INTERPOLATE_ALL_LAYERS) && (gpl != active_gpl)) { continue; @@ -483,6 +476,11 @@ static bool gpencil_interpolate_set_init_values(bContext *C, wmOperator *op, tGP /* set layers */ gpencil_interpolate_set_points(C, tgpi); + /* Untag strokes to be sure nothing is pending due any canceled process. */ + LISTBASE_FOREACH (bGPDlayer *, gpl, &tgpi->gpd->layers) { + gpencil_interpolate_untag_strokes(gpl); + } + return 1; } diff --git a/source/blender/editors/gpencil/gpencil_ops.c b/source/blender/editors/gpencil/gpencil_ops.c index efee05f7da3..3892f451e18 100644 --- a/source/blender/editors/gpencil/gpencil_ops.c +++ b/source/blender/editors/gpencil/gpencil_ops.c @@ -600,6 +600,7 @@ void ED_operatortypes_gpencil(void) WM_operatortype_append(GPENCIL_OT_frame_duplicate); WM_operatortype_append(GPENCIL_OT_frame_clean_fill); WM_operatortype_append(GPENCIL_OT_frame_clean_loose); + WM_operatortype_append(GPENCIL_OT_frame_clean_duplicate); WM_operatortype_append(GPENCIL_OT_convert); WM_operatortype_append(GPENCIL_OT_bake_mesh_animation); diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c index d3ff7f8a4d2..b61baf3d0b1 100644 --- a/source/blender/editors/gpencil/gpencil_paint.c +++ b/source/blender/editors/gpencil/gpencil_paint.c @@ -268,7 +268,7 @@ typedef struct tGPsdata { /* Macros for accessing sensitivity thresholds... */ /* minimum number of pixels mouse should move before new point created */ -#define MIN_MANHATTEN_PX (U.gp_manhattendist) +#define MIN_MANHATTEN_PX (U.gp_manhattandist) /* minimum length of new segment before new point can be added */ #define MIN_EUCLIDEAN_PX (U.gp_euclideandist) diff --git a/source/blender/editors/include/BIF_glutil.h b/source/blender/editors/include/BIF_glutil.h index 1eb22fb34e2..cfd92bf3113 100644 --- a/source/blender/editors/include/BIF_glutil.h +++ b/source/blender/editors/include/BIF_glutil.h @@ -24,6 +24,8 @@ #ifndef __BIF_GLUTIL_H__ #define __BIF_GLUTIL_H__ +#include "GPU_texture.h" + #ifdef __cplusplus extern "C" { #endif @@ -66,9 +68,8 @@ void immDrawPixelsTex(IMMDrawPixelsTexState *state, float y, int img_w, int img_h, - int format, - int type, - int zoomfilter, + eGPUTextureFormat gpu_format, + bool use_filter, void *rect, float xzoom, float yzoom, @@ -78,9 +79,8 @@ void immDrawPixelsTex_clipping(IMMDrawPixelsTexState *state, float y, int img_w, int img_h, - int format, - int type, - int zoomfilter, + eGPUTextureFormat gpu_format, + bool use_filter, void *rect, float clip_min_x, float clip_min_y, @@ -94,9 +94,8 @@ void immDrawPixelsTexScaled(IMMDrawPixelsTexState *state, float y, int img_w, int img_h, - int format, - int type, - int zoomfilter, + eGPUTextureFormat gpu_format, + bool use_filter, void *rect, float scaleX, float scaleY, @@ -108,9 +107,8 @@ void immDrawPixelsTexScaled_clipping(IMMDrawPixelsTexState *state, float y, int img_w, int img_h, - int format, - int type, - int zoomfilter, + eGPUTextureFormat gpu_format, + bool use_filter, void *rect, float scaleX, float scaleY, @@ -133,7 +131,7 @@ void immDrawPixelsTexScaled_clipping(IMMDrawPixelsTexState *state, void ED_draw_imbuf(struct ImBuf *ibuf, float x, float y, - int zoomfilter, + bool use_filter, struct ColorManagedViewSettings *view_settings, struct ColorManagedDisplaySettings *display_settings, float zoom_x, @@ -141,7 +139,7 @@ void ED_draw_imbuf(struct ImBuf *ibuf, void ED_draw_imbuf_clipping(struct ImBuf *ibuf, float x, float y, - int zoomfilter, + bool use_filter, struct ColorManagedViewSettings *view_settings, struct ColorManagedDisplaySettings *display_settings, float clip_min_x, @@ -155,14 +153,14 @@ void ED_draw_imbuf_ctx(const struct bContext *C, struct ImBuf *ibuf, float x, float y, - int zoomfilter, + bool use_filter, float zoom_x, float zoom_y); void ED_draw_imbuf_ctx_clipping(const struct bContext *C, struct ImBuf *ibuf, float x, float y, - int zoomfilter, + bool use_filter, float clip_min_x, float clip_min_y, float clip_max_x, diff --git a/source/blender/editors/include/ED_buttons.h b/source/blender/editors/include/ED_buttons.h index 2eaef5e82e0..455eee8580d 100644 --- a/source/blender/editors/include/ED_buttons.h +++ b/source/blender/editors/include/ED_buttons.h @@ -27,6 +27,10 @@ extern "C" { #endif +struct SpaceProperties; + +int ED_buttons_tabs_list(struct SpaceProperties *sbuts, int *context_tabs_array); + #ifdef __cplusplus } #endif diff --git a/source/blender/editors/include/ED_numinput.h b/source/blender/editors/include/ED_numinput.h index 8c8f3e6f4a3..16d05a7793a 100644 --- a/source/blender/editors/include/ED_numinput.h +++ b/source/blender/editors/include/ED_numinput.h @@ -103,8 +103,12 @@ bool handleNumInput(struct bContext *C, NumInput *n, const struct wmEvent *event #define NUM_MODAL_INCREMENT_UP 18 #define NUM_MODAL_INCREMENT_DOWN 19 -bool user_string_to_number( - bContext *C, const char *str, const struct UnitSettings *unit, int type, double *r_value); +bool user_string_to_number(bContext *C, + const char *str, + const struct UnitSettings *unit, + int type, + const char *error_prefix, + double *r_value); /** \} */ diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index 12ff147b0a1..9441e48ef24 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -52,7 +52,6 @@ #include "BKE_screen.h" #include "BKE_unit.h" -#include "GPU_glew.h" #include "GPU_matrix.h" #include "GPU_state.h" @@ -2801,25 +2800,39 @@ char *ui_but_string_get_dynamic(uiBut *but, int *r_str_size) return str; } -static bool ui_set_but_string_eval_num_unit(bContext *C, - uiBut *but, - const char *str, - double *r_value) +/** + * Report a generic error prefix when evaluating a string with #BPY_execute_string_as_number + * as the Python error on it's own doesn't provide enough context. + */ +#define UI_NUMBER_EVAL_ERROR_PREFIX IFACE_("Error evaluating number, see Info editor for details") + +static bool ui_number_from_string_units( + bContext *C, const char *str, const int unit_type, const UnitSettings *unit, double *r_value) { + return user_string_to_number(C, str, unit, unit_type, UI_NUMBER_EVAL_ERROR_PREFIX, r_value); +} + +static bool ui_number_from_string_units_with_but(bContext *C, + const char *str, + const uiBut *but, + double *r_value) +{ + const int unit_type = RNA_SUBTYPE_UNIT_VALUE(UI_but_unit_type_get(but)); const UnitSettings *unit = but->block->unit; - int type = RNA_SUBTYPE_UNIT_VALUE(UI_but_unit_type_get(but)); - return user_string_to_number(C, str, unit, type, r_value); + return ui_number_from_string_units(C, str, unit_type, unit, r_value); } static bool ui_number_from_string(bContext *C, const char *str, double *r_value) { + bool ok; #ifdef WITH_PYTHON - return BPY_execute_string_as_number(C, NULL, str, true, r_value); + ok = BPY_execute_string_as_number(C, NULL, str, UI_NUMBER_EVAL_ERROR_PREFIX, r_value); #else UNUSED_VARS(C); *r_value = atof(str); - return true; + ok = true; #endif + return ok; } static bool ui_number_from_string_factor(bContext *C, const char *str, double *r_value) @@ -2853,7 +2866,7 @@ static bool ui_number_from_string_percentage(bContext *C, const char *str, doubl return ui_number_from_string(C, str, r_value); } -bool ui_but_string_set_eval_num(bContext *C, uiBut *but, const char *str, double *r_value) +bool ui_but_string_eval_number(bContext *C, const uiBut *but, const char *str, double *r_value) { if (str[0] == '\0') { *r_value = 0.0; @@ -2867,7 +2880,7 @@ bool ui_but_string_set_eval_num(bContext *C, uiBut *but, const char *str, double if (ui_but_is_float(but)) { if (ui_but_is_unit(but)) { - return ui_set_but_string_eval_num_unit(C, but, str, r_value); + return ui_number_from_string_units_with_but(C, str, but, r_value); } if (subtype == PROP_FACTOR) { return ui_number_from_string_factor(C, str, r_value); @@ -3007,7 +3020,7 @@ bool ui_but_string_set(bContext *C, uiBut *but, const char *str) /* number editing */ double value; - if (ui_but_string_set_eval_num(C, but, str, &value) == false) { + if (ui_but_string_eval_number(C, but, str, &value) == false) { WM_report_banner_show(); return false; } diff --git a/source/blender/editors/interface/interface_context_menu.c b/source/blender/editors/interface/interface_context_menu.c index a08c5c45b6f..aaa5e1c0cf1 100644 --- a/source/blender/editors/interface/interface_context_menu.c +++ b/source/blender/editors/interface/interface_context_menu.c @@ -407,7 +407,7 @@ static void ui_but_user_menu_add(bContext *C, uiBut *but, bUserMenu *um) "'%s').label", idname); char *expr_result = NULL; - if (BPY_execute_string_as_string(C, expr_imports, expr, true, &expr_result)) { + if (BPY_execute_string_as_string(C, expr_imports, expr, __func__, &expr_result)) { STRNCPY(drawstr, expr_result); MEM_freeN(expr_result); } diff --git a/source/blender/editors/interface/interface_draw.c b/source/blender/editors/interface/interface_draw.c index cc5d21c3df3..947b6e7ee9d 100644 --- a/source/blender/editors/interface/interface_draw.c +++ b/source/blender/editors/interface/interface_draw.c @@ -773,9 +773,8 @@ void ui_draw_but_IMAGE(ARegion *UNUSED(region), (float)rect->ymin, ibuf->x, ibuf->y, - GL_RGBA, - GL_UNSIGNED_BYTE, - GL_NEAREST, + GPU_RGBA8, + false, ibuf->rect, 1.0f, 1.0f, @@ -2545,9 +2544,8 @@ void ui_draw_but_TRACKPREVIEW(ARegion *UNUSED(region), rect.ymin + 1, drawibuf->x, drawibuf->y, - GL_RGBA, - GL_UNSIGNED_BYTE, - GL_LINEAR, + GPU_RGBA8, + true, drawibuf->rect, 1.0f, 1.0f, diff --git a/source/blender/editors/interface/interface_eyedropper_color.c b/source/blender/editors/interface/interface_eyedropper_color.c index 93b052b3b69..5da82b5be9c 100644 --- a/source/blender/editors/interface/interface_eyedropper_color.c +++ b/source/blender/editors/interface/interface_eyedropper_color.c @@ -39,8 +39,6 @@ #include "RNA_access.h" -#include "GPU_glew.h" - #include "UI_interface.h" #include "IMB_colormanagement.h" diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 88ad5d8fec1..1128cceb6e3 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -2364,7 +2364,7 @@ static void ui_but_paste_numeric_value(bContext *C, { double value; - if (ui_but_string_set_eval_num(C, but, buf_paste, &value)) { + if (ui_but_string_eval_number(C, but, buf_paste, &value)) { button_activate_state(C, but, BUTTON_STATE_NUM_EDITING); data->value = value; ui_but_string_set(C, but, buf_paste); diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c index 586f5e07997..a7b7bad2fe6 100644 --- a/source/blender/editors/interface/interface_icons.c +++ b/source/blender/editors/interface/interface_icons.c @@ -1517,18 +1517,8 @@ static void icon_draw_rect(float x, immUniform1f("factor", desaturate); } - immDrawPixelsTex(&state, - draw_x, - draw_y, - draw_w, - draw_h, - GL_RGBA, - GL_UNSIGNED_BYTE, - GL_NEAREST, - rect, - 1.0f, - 1.0f, - col); + immDrawPixelsTex( + &state, draw_x, draw_y, draw_w, draw_h, GPU_RGBA8, false, rect, 1.0f, 1.0f, col); if (ima) { IMB_freeImBuf(ima); diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index 249134c6abf..49d9561a75f 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -517,10 +517,10 @@ extern void ui_but_string_get(uiBut *but, char *str, const size_t maxlen) ATTR_N extern char *ui_but_string_get_dynamic(uiBut *but, int *r_str_size); extern void ui_but_convert_to_unit_alt_name(uiBut *but, char *str, size_t maxlen) ATTR_NONNULL(); extern bool ui_but_string_set(struct bContext *C, uiBut *but, const char *str) ATTR_NONNULL(); -extern bool ui_but_string_set_eval_num(struct bContext *C, - uiBut *but, - const char *str, - double *value) ATTR_NONNULL(); +extern bool ui_but_string_eval_number(struct bContext *C, + const uiBut *but, + const char *str, + double *value) ATTR_NONNULL(); extern int ui_but_string_get_max_length(uiBut *but); /* Clear & exit the active button's string. */ extern void ui_but_active_string_clear_and_exit(struct bContext *C, uiBut *but) ATTR_NONNULL(); diff --git a/source/blender/editors/interface/interface_region_tooltip.c b/source/blender/editors/interface/interface_region_tooltip.c index 46814e11b9e..7c64e4c2709 100644 --- a/source/blender/editors/interface/interface_region_tooltip.c +++ b/source/blender/editors/interface/interface_region_tooltip.c @@ -433,7 +433,7 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is if (has_valid_context == false) { expr_result = BLI_strdup(has_valid_context_error); } - else if (BPY_execute_string_as_string(C, expr_imports, expr, true, &expr_result)) { + else if (BPY_execute_string_as_string(C, expr_imports, expr, __func__, &expr_result)) { if (STREQ(expr_result, "")) { MEM_freeN(expr_result); expr_result = NULL; @@ -490,7 +490,7 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is if (has_valid_context == false) { expr_result = BLI_strdup(has_valid_context_error); } - else if (BPY_execute_string_as_string(C, expr_imports, expr, true, &expr_result)) { + else if (BPY_execute_string_as_string(C, expr_imports, expr, __func__, &expr_result)) { if (STREQ(expr_result, ".")) { MEM_freeN(expr_result); expr_result = NULL; @@ -594,7 +594,7 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is if (has_valid_context == false) { shortcut = BLI_strdup(has_valid_context_error); } - else if (BPY_execute_string_as_intptr(C, expr_imports, expr, true, &expr_result)) { + else if (BPY_execute_string_as_intptr(C, expr_imports, expr, __func__, &expr_result)) { if (expr_result != 0) { wmKeyMap *keymap = (wmKeyMap *)expr_result; LISTBASE_FOREACH (wmKeyMapItem *, kmi, &keymap->items) { @@ -659,7 +659,7 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is /* pass */ } else if (BPY_execute_string_as_string_and_size( - C, expr_imports, expr, true, &expr_result, &expr_result_len)) { + C, expr_imports, expr, __func__, &expr_result, &expr_result_len)) { /* pass. */ } } @@ -736,7 +736,7 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is if (has_valid_context == false) { /* pass */ } - else if (BPY_execute_string_as_intptr(C, expr_imports, expr, true, &expr_result)) { + else if (BPY_execute_string_as_intptr(C, expr_imports, expr, __func__, &expr_result)) { if (expr_result != 0) { { uiTooltipField *field = text_field_add(data, diff --git a/source/blender/editors/mask/mask_draw.c b/source/blender/editors/mask/mask_draw.c index 3786ed2789c..12ce358a501 100644 --- a/source/blender/editors/mask/mask_draw.c +++ b/source/blender/editors/mask/mask_draw.c @@ -753,8 +753,7 @@ void ED_mask_draw_region( IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_SHUFFLE_COLOR); GPU_shader_uniform_vector( state.shader, GPU_shader_get_uniform(state.shader, "shuffle"), 4, 1, red); - immDrawPixelsTex( - &state, 0.0f, 0.0f, width, height, GL_RED, GL_FLOAT, GL_NEAREST, buffer, 1.0f, 1.0f, NULL); + immDrawPixelsTex(&state, 0.0f, 0.0f, width, height, GL_R16F, false, buffer, 1.0f, 1.0f, NULL); GPU_matrix_pop(); diff --git a/source/blender/editors/mesh/editmesh_knife.c b/source/blender/editors/mesh/editmesh_knife.c index 347f6806d13..1f411617c68 100644 --- a/source/blender/editors/mesh/editmesh_knife.c +++ b/source/blender/editors/mesh/editmesh_knife.c @@ -1628,9 +1628,9 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd) plane_from_point_normal_v3(plane, v1, plane); } - /* First use bvh tree to find faces, knife edges, and knife verts that might + /* First use BVH tree to find faces, knife edges, and knife verts that might * intersect the cut plane with rays v1-v3 and v2-v4. - * This deduplicates the candidates before doing more expensive intersection tests. */ + * This de-duplicates the candidates before doing more expensive intersection tests. */ tree = BKE_bmbvh_tree_get(kcd->bmbvh); results = BLI_bvhtree_intersect_plane(tree, plane, &tot); diff --git a/source/blender/editors/mesh/editmesh_select.c b/source/blender/editors/mesh/editmesh_select.c index 1ea25353598..d2e9b57e950 100644 --- a/source/blender/editors/mesh/editmesh_select.c +++ b/source/blender/editors/mesh/editmesh_select.c @@ -205,7 +205,7 @@ static BMElem *edbm_select_id_bm_elem_get(Base **bases, const uint sel_id, uint /* -------------------------------------------------------------------- */ /** \name Find Nearest Vert/Edge/Face * - * \note Screen-space manhatten distances are used here, + * \note Screen-space manhattan distances are used here, * since its faster and good enough for the purpose of selection. * * \note \a dist_bias is used so we can bias against selected items. @@ -415,7 +415,7 @@ struct NearestEdgeUserData_Hit { int index; BMEdge *edge; - /* edges only, un-biased manhatten distance to which ever edge we pick + /* edges only, un-biased manhattan distance to which ever edge we pick * (not used for choosing) */ float dist_center; }; diff --git a/source/blender/editors/render/render_opengl.c b/source/blender/editors/render/render_opengl.c index 673df69bf9b..3d3936523f9 100644 --- a/source/blender/editors/render/render_opengl.c +++ b/source/blender/editors/render/render_opengl.c @@ -79,7 +79,6 @@ #include "RNA_define.h" #include "GPU_framebuffer.h" -#include "GPU_glew.h" #include "GPU_matrix.h" #include "render_intern.h" @@ -351,7 +350,7 @@ static void screen_opengl_render_doit(const bContext *C, OGLRender *oglrender, R G.f &= ~G_FLAG_RENDER_VIEWPORT; gp_rect = MEM_mallocN(sizex * sizey * sizeof(uchar) * 4, "offscreen rect"); - GPU_offscreen_read_pixels(oglrender->ofs, GL_UNSIGNED_BYTE, gp_rect); + GPU_offscreen_read_pixels(oglrender->ofs, GPU_DATA_UNSIGNED_BYTE, gp_rect); for (i = 0; i < sizex * sizey * 4; i += 4) { blend_color_mix_byte(&render_rect[i], &render_rect[i], &gp_rect[i]); diff --git a/source/blender/editors/render/render_preview.c b/source/blender/editors/render/render_preview.c index 19e4f652963..85fc6927063 100644 --- a/source/blender/editors/render/render_preview.c +++ b/source/blender/editors/render/render_preview.c @@ -82,7 +82,6 @@ #include "BIF_glutil.h" -#include "GPU_glew.h" #include "GPU_shader.h" #include "RE_engine.h" @@ -632,18 +631,8 @@ static bool ed_preview_draw_rect(ScrArea *area, int split, int first, rcti *rect } IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_COLOR); - immDrawPixelsTex(&state, - fx, - fy, - rres.rectx, - rres.recty, - GL_RGBA, - GL_UNSIGNED_BYTE, - GL_NEAREST, - rect_byte, - 1.0f, - 1.0f, - NULL); + immDrawPixelsTex( + &state, fx, fy, rres.rectx, rres.recty, GPU_RGBA8, false, rect_byte, 1.0f, 1.0f, NULL); MEM_freeN(rect_byte); diff --git a/source/blender/editors/screen/glutil.c b/source/blender/editors/screen/glutil.c index 5c3b1944164..07a122c7094 100644 --- a/source/blender/editors/screen/glutil.c +++ b/source/blender/editors/screen/glutil.c @@ -95,9 +95,8 @@ void immDrawPixelsTexScaled_clipping(IMMDrawPixelsTexState *state, float y, int img_w, int img_h, - int format, - int type, - int zoomfilter, + eGPUTextureFormat gpu_format, + bool use_filter, void *rect, float scaleX, float scaleY, @@ -115,21 +114,30 @@ void immDrawPixelsTexScaled_clipping(IMMDrawPixelsTexState *state, const bool use_clipping = ((clip_min_x < clip_max_x) && (clip_min_y < clip_max_y)); float white[4] = {1.0f, 1.0f, 1.0f, 1.0f}; - if (type != GL_FLOAT) { - BLI_assert(type == GL_UNSIGNED_BYTE); - type = GL_UNSIGNED_BYTE; + if (ELEM(gpu_format, GPU_RGBA8, GPU_RGBA16F)) { + components = 4; + } + else if (ELEM(gpu_format, GPU_RGB16F)) { + components = 3; + } + else if (ELEM(gpu_format, GPU_R8, GPU_R16F)) { + components = 1; + } + else { + BLI_assert(!"Incompatible format passed to immDrawPixels"); + return; } - eGPUTextureFormat gpu_format = (type == GL_FLOAT) ? GPU_RGBA16F : GPU_RGBA8; - eGPUDataFormat gpu_data = (type == GL_FLOAT) ? GPU_DATA_FLOAT : GPU_DATA_UNSIGNED_BYTE; - GPUTexture *texture = GPU_texture_create_nD( - tex_w, tex_h, 0, 2, NULL, gpu_format, gpu_data, 0, false, NULL); + const bool use_float_data = ELEM(gpu_format, GPU_RGBA16F, GPU_RGB16F, GPU_R16F); + eGPUDataFormat gpu_data = (use_float_data) ? GPU_DATA_FLOAT : GPU_DATA_UNSIGNED_BYTE; + size_t stride = components * ((use_float_data) ? sizeof(float) : sizeof(uchar)); - /* TODO replace GL_NEAREST/LINEAR in callers. */ - GPU_texture_filter_mode(texture, (zoomfilter == GL_LINEAR)); - GPU_texture_wrap_mode(texture, false, true); + GPUTexture *tex = GPU_texture_create_2d(tex_w, tex_h, gpu_format, NULL, NULL); - GPU_texture_bind(texture, 0); + GPU_texture_filter_mode(tex, use_filter); + GPU_texture_wrap_mode(tex, false, true); + + GPU_texture_bind(tex, 0); /* setup seamless 2=on, 0=off */ seamless = ((tex_w < img_w || tex_h < img_h) && tex_w > 2 && tex_h > 2) ? 2 : 0; @@ -140,20 +148,6 @@ void immDrawPixelsTexScaled_clipping(IMMDrawPixelsTexState *state, nsubparts_x = (img_w + (offset_x - 1)) / (offset_x); nsubparts_y = (img_h + (offset_y - 1)) / (offset_y); - if (format == GL_RGBA) { - components = 4; - } - else if (format == GL_RGB) { - components = 3; - } - else if (format == GL_RED) { - components = 1; - } - else { - BLI_assert(!"Incompatible format passed to glaDrawPixelsTexScaled"); - return; - } - /* optional */ /* NOTE: Shader could be null for GLSL OCIO drawing, it is fine, since * it does not need color. @@ -199,26 +193,32 @@ void immDrawPixelsTexScaled_clipping(IMMDrawPixelsTexState *state, { int src_y = subpart_y * offset_y; int src_x = subpart_x * offset_x; - size_t stride = components * ((type == GL_FLOAT) ? sizeof(float) : sizeof(uchar)); #define DATA(_y, _x) ((char *)rect + stride * ((size_t)(_y)*img_w + (_x))) { void *data = DATA(src_y, src_x); - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, subpart_w, subpart_h, format, type, data); + GPU_texture_update_sub(tex, gpu_data, data, 0, 0, 0, subpart_w, subpart_h, 0); } /* Add an extra border of pixels so linear interpolation looks ok * at edges of full image. */ if (subpart_w < tex_w) { void *data = DATA(src_y, src_x + subpart_w - 1); - glTexSubImage2D(GL_TEXTURE_2D, 0, subpart_w, 0, 1, subpart_h, format, type, data); + int offset[2] = {subpart_w, 0}; + int extent[2] = {1, subpart_h}; + GPU_texture_update_sub(tex, gpu_data, data, UNPACK2(offset), 0, UNPACK2(extent), 0); } if (subpart_h < tex_h) { void *data = DATA(src_y + subpart_h - 1, src_x); - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, subpart_h, subpart_w, 1, format, type, data); + int offset[2] = {0, subpart_h}; + int extent[2] = {subpart_w, 1}; + GPU_texture_update_sub(tex, gpu_data, data, UNPACK2(offset), 0, UNPACK2(extent), 0); } + if (subpart_w < tex_w && subpart_h < tex_h) { void *data = DATA(src_y + subpart_h - 1, src_x + subpart_w - 1); - glTexSubImage2D(GL_TEXTURE_2D, 0, subpart_w, subpart_h, 1, 1, format, type, data); + int offset[2] = {subpart_w, subpart_h}; + int extent[2] = {1, 1}; + GPU_texture_update_sub(tex, gpu_data, data, UNPACK2(offset), 0, UNPACK2(extent), 0); } #undef DATA } @@ -253,8 +253,8 @@ void immDrawPixelsTexScaled_clipping(IMMDrawPixelsTexState *state, immUnbindProgram(); } - GPU_texture_unbind(texture); - GPU_texture_free(texture); + GPU_texture_unbind(tex); + GPU_texture_free(tex); /* Restore default. */ GPU_unpack_row_length_set(0); @@ -265,9 +265,8 @@ void immDrawPixelsTexScaled(IMMDrawPixelsTexState *state, float y, int img_w, int img_h, - int format, - int type, - int zoomfilter, + eGPUTextureFormat gpu_format, + bool use_filter, void *rect, float scaleX, float scaleY, @@ -280,9 +279,8 @@ void immDrawPixelsTexScaled(IMMDrawPixelsTexState *state, y, img_w, img_h, - format, - type, - zoomfilter, + gpu_format, + use_filter, rect, scaleX, scaleY, @@ -300,9 +298,8 @@ void immDrawPixelsTex(IMMDrawPixelsTexState *state, float y, int img_w, int img_h, - int format, - int type, - int zoomfilter, + eGPUTextureFormat gpu_format, + bool use_filter, void *rect, float xzoom, float yzoom, @@ -313,9 +310,8 @@ void immDrawPixelsTex(IMMDrawPixelsTexState *state, y, img_w, img_h, - format, - type, - zoomfilter, + gpu_format, + use_filter, rect, 1.0f, 1.0f, @@ -333,9 +329,8 @@ void immDrawPixelsTex_clipping(IMMDrawPixelsTexState *state, float y, int img_w, int img_h, - int format, - int type, - int zoomfilter, + eGPUTextureFormat gpu_format, + bool use_filter, void *rect, float clip_min_x, float clip_min_y, @@ -350,9 +345,8 @@ void immDrawPixelsTex_clipping(IMMDrawPixelsTexState *state, y, img_w, img_h, - format, - type, - zoomfilter, + gpu_format, + use_filter, rect, 1.0f, 1.0f, @@ -371,7 +365,7 @@ void immDrawPixelsTex_clipping(IMMDrawPixelsTexState *state, void ED_draw_imbuf_clipping(ImBuf *ibuf, float x, float y, - int zoomfilter, + bool use_filter, ColorManagedViewSettings *view_settings, ColorManagedDisplaySettings *display_settings, float clip_min_x, @@ -421,13 +415,13 @@ void ED_draw_imbuf_clipping(ImBuf *ibuf, if (ok) { if (ibuf->rect_float) { - int format = 0; + eGPUTextureFormat format = 0; if (ibuf->channels == 3) { - format = GL_RGB; + format = GPU_RGB16F; } else if (ibuf->channels == 4) { - format = GL_RGBA; + format = GPU_RGBA16F; } else { BLI_assert(!"Incompatible number of channels for GLSL display"); @@ -440,8 +434,7 @@ void ED_draw_imbuf_clipping(ImBuf *ibuf, ibuf->x, ibuf->y, format, - GL_FLOAT, - zoomfilter, + use_filter, ibuf->rect_float, clip_min_x, clip_min_y, @@ -459,9 +452,8 @@ void ED_draw_imbuf_clipping(ImBuf *ibuf, y, ibuf->x, ibuf->y, - GL_RGBA, - GL_UNSIGNED_BYTE, - zoomfilter, + GPU_RGBA8, + use_filter, ibuf->rect, clip_min_x, clip_min_y, @@ -493,9 +485,8 @@ void ED_draw_imbuf_clipping(ImBuf *ibuf, y, ibuf->x, ibuf->y, - GL_RGBA, - GL_UNSIGNED_BYTE, - zoomfilter, + GPU_RGBA8, + use_filter, display_buffer, clip_min_x, clip_min_y, @@ -513,7 +504,7 @@ void ED_draw_imbuf_clipping(ImBuf *ibuf, void ED_draw_imbuf(ImBuf *ibuf, float x, float y, - int zoomfilter, + bool use_filter, ColorManagedViewSettings *view_settings, ColorManagedDisplaySettings *display_settings, float zoom_x, @@ -522,7 +513,7 @@ void ED_draw_imbuf(ImBuf *ibuf, ED_draw_imbuf_clipping(ibuf, x, y, - zoomfilter, + use_filter, view_settings, display_settings, 0.0f, @@ -537,7 +528,7 @@ void ED_draw_imbuf_ctx_clipping(const bContext *C, ImBuf *ibuf, float x, float y, - int zoomfilter, + bool use_filter, float clip_min_x, float clip_min_y, float clip_max_x, @@ -553,7 +544,7 @@ void ED_draw_imbuf_ctx_clipping(const bContext *C, ED_draw_imbuf_clipping(ibuf, x, y, - zoomfilter, + use_filter, view_settings, display_settings, clip_min_x, @@ -565,9 +556,9 @@ void ED_draw_imbuf_ctx_clipping(const bContext *C, } void ED_draw_imbuf_ctx( - const bContext *C, ImBuf *ibuf, float x, float y, int zoomfilter, float zoom_x, float zoom_y) + const bContext *C, ImBuf *ibuf, float x, float y, bool use_filter, float zoom_x, float zoom_y) { - ED_draw_imbuf_ctx_clipping(C, ibuf, x, y, zoomfilter, 0.0f, 0.0f, 0.0f, 0.0f, zoom_x, zoom_y); + ED_draw_imbuf_ctx_clipping(C, ibuf, x, y, use_filter, 0.0f, 0.0f, 0.0f, 0.0f, zoom_x, zoom_y); } int ED_draw_imbuf_method(ImBuf *ibuf) diff --git a/source/blender/editors/screen/screen_draw.c b/source/blender/editors/screen/screen_draw.c index 1608e842376..40a452a5363 100644 --- a/source/blender/editors/screen/screen_draw.c +++ b/source/blender/editors/screen/screen_draw.c @@ -619,7 +619,7 @@ void ED_screen_preview_render(const bScreen *screen, int size_x, int size_y, uin screen_preview_draw(screen, size_x, size_y); - GPU_offscreen_read_pixels(offscreen, GL_UNSIGNED_BYTE, r_rect); + GPU_offscreen_read_pixels(offscreen, GPU_DATA_UNSIGNED_BYTE, r_rect); GPU_offscreen_unbind(offscreen, true); GPU_offscreen_free(offscreen); diff --git a/source/blender/editors/space_buttons/space_buttons.c b/source/blender/editors/space_buttons/space_buttons.c index 88c2c6e82b6..67efd8f4b8e 100644 --- a/source/blender/editors/space_buttons/space_buttons.c +++ b/source/blender/editors/space_buttons/space_buttons.c @@ -35,6 +35,7 @@ #include "BKE_screen.h" #include "BKE_shader_fx.h" +#include "ED_buttons.h" #include "ED_screen.h" #include "ED_space_api.h" #include "ED_view3d.h" /* To draw toolbar UI. */ @@ -49,8 +50,6 @@ #include "UI_resources.h" -#include "GPU_glew.h" - #include "buttons_intern.h" /* own include */ /* ******************** default callbacks for buttons space ***************** */ @@ -139,6 +138,98 @@ static void buttons_main_region_init(wmWindowManager *wm, ARegion *region) WM_event_add_keymap_handler(®ion->handlers, keymap); } +/** + * Fills an array with the tab context values for the properties editor. -1 signals a separator. + * + * \return The total number of items in the array returned. + */ +int ED_buttons_tabs_list(SpaceProperties *sbuts, int *context_tabs_array) +{ + int length = 0; + if (sbuts->pathflag & (1 << BCONTEXT_TOOL)) { + context_tabs_array[length] = BCONTEXT_TOOL; + length++; + } + if (length != 0) { + context_tabs_array[length] = -1; + length++; + } + if (sbuts->pathflag & (1 << BCONTEXT_RENDER)) { + context_tabs_array[length] = BCONTEXT_RENDER; + length++; + } + if (sbuts->pathflag & (1 << BCONTEXT_OUTPUT)) { + context_tabs_array[length] = BCONTEXT_OUTPUT; + length++; + } + if (sbuts->pathflag & (1 << BCONTEXT_VIEW_LAYER)) { + context_tabs_array[length] = BCONTEXT_VIEW_LAYER; + length++; + } + if (sbuts->pathflag & (1 << BCONTEXT_SCENE)) { + context_tabs_array[length] = BCONTEXT_SCENE; + length++; + } + if (sbuts->pathflag & (1 << BCONTEXT_WORLD)) { + context_tabs_array[length] = BCONTEXT_WORLD; + length++; + } + if (length != 0) { + context_tabs_array[length] = -1; + length++; + } + if (sbuts->pathflag & (1 << BCONTEXT_OBJECT)) { + context_tabs_array[length] = BCONTEXT_OBJECT; + length++; + } + if (sbuts->pathflag & (1 << BCONTEXT_MODIFIER)) { + context_tabs_array[length] = BCONTEXT_MODIFIER; + length++; + } + if (sbuts->pathflag & (1 << BCONTEXT_SHADERFX)) { + context_tabs_array[length] = BCONTEXT_SHADERFX; + length++; + } + if (sbuts->pathflag & (1 << BCONTEXT_PARTICLE)) { + context_tabs_array[length] = BCONTEXT_PARTICLE; + length++; + } + if (sbuts->pathflag & (1 << BCONTEXT_PHYSICS)) { + context_tabs_array[length] = BCONTEXT_PHYSICS; + length++; + } + if (sbuts->pathflag & (1 << BCONTEXT_CONSTRAINT)) { + context_tabs_array[length] = BCONTEXT_CONSTRAINT; + length++; + } + if (sbuts->pathflag & (1 << BCONTEXT_DATA)) { + context_tabs_array[length] = BCONTEXT_DATA; + length++; + } + if (sbuts->pathflag & (1 << BCONTEXT_BONE)) { + context_tabs_array[length] = BCONTEXT_BONE; + length++; + } + if (sbuts->pathflag & (1 << BCONTEXT_BONE_CONSTRAINT)) { + context_tabs_array[length] = BCONTEXT_BONE_CONSTRAINT; + length++; + } + if (sbuts->pathflag & (1 << BCONTEXT_MATERIAL)) { + context_tabs_array[length] = BCONTEXT_MATERIAL; + length++; + } + if (length != 0) { + context_tabs_array[length] = -1; + length++; + } + if (sbuts->pathflag & (1 << BCONTEXT_TEXTURE)) { + context_tabs_array[length] = BCONTEXT_TEXTURE; + length++; + } + + return length; +} + static void buttons_main_region_layout_properties(const bContext *C, SpaceProperties *sbuts, ARegion *region) diff --git a/source/blender/editors/space_clip/clip_draw.c b/source/blender/editors/space_clip/clip_draw.c index d33f624063a..1d510d2989c 100644 --- a/source/blender/editors/space_clip/clip_draw.c +++ b/source/blender/editors/space_clip/clip_draw.c @@ -322,7 +322,7 @@ static void draw_movieclip_buffer(const bContext *C, float zoomy) { MovieClip *clip = ED_space_clip_get_clip(sc); - int filter = GL_LINEAR; + bool use_filter = true; int x, y; /* find window pixel coordinates of origin */ @@ -340,10 +340,10 @@ static void draw_movieclip_buffer(const bContext *C, /* non-scaled proxy shouldn't use filtering */ if ((clip->flag & MCLIP_USE_PROXY) == 0 || ELEM(sc->user.render_size, MCLIP_PROXY_RENDER_SIZE_FULL, MCLIP_PROXY_RENDER_SIZE_100)) { - filter = GL_NEAREST; + use_filter = false; } - ED_draw_imbuf_ctx(C, ibuf, x, y, filter, zoomx * width / ibuf->x, zoomy * height / ibuf->y); + ED_draw_imbuf_ctx(C, ibuf, x, y, use_filter, zoomx * width / ibuf->x, zoomy * height / ibuf->y); if (ibuf->planes == 32) { GPU_blend(false); diff --git a/source/blender/editors/space_clip/space_clip.c b/source/blender/editors/space_clip/space_clip.c index 9c251fb619a..37d5f602291 100644 --- a/source/blender/editors/space_clip/space_clip.c +++ b/source/blender/editors/space_clip/space_clip.c @@ -56,7 +56,6 @@ #include "IMB_imbuf.h" #include "GPU_framebuffer.h" -#include "GPU_glew.h" #include "GPU_matrix.h" #include "WM_api.h" diff --git a/source/blender/editors/space_file/file_draw.c b/source/blender/editors/space_file/file_draw.c index 8d14664c0fa..68e9f76abc5 100644 --- a/source/blender/editors/space_file/file_draw.c +++ b/source/blender/editors/space_file/file_draw.c @@ -300,9 +300,8 @@ static void file_draw_preview(uiBlock *block, (float)yco, imb->x, imb->y, - GL_RGBA, - GL_UNSIGNED_BYTE, - GL_NEAREST, + GPU_RGBA8, + false, imb->rect, scale, scale, diff --git a/source/blender/editors/space_image/image_draw.c b/source/blender/editors/space_image/image_draw.c index a7fa7709c51..1038011e480 100644 --- a/source/blender/editors/space_image/image_draw.c +++ b/source/blender/editors/space_image/image_draw.c @@ -468,20 +468,19 @@ static void sima_draw_zbuf_pixels( float red[4] = {1.0f, 0.0f, 0.0f, 0.0f}; /* Slowwww */ - int *recti = MEM_mallocN(rectx * recty * sizeof(int), "temp"); + float *rectf = MEM_mallocN(rectx * recty * sizeof(float), "temp"); for (int a = rectx * recty - 1; a >= 0; a--) { /* zbuffer values are signed, so we need to shift color range */ - recti[a] = rect[a] * 0.5f + 0.5f; + rectf[a] = rect[a] * 0.5f + 0.5f; } IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_SHUFFLE_COLOR); GPU_shader_uniform_vector( state.shader, GPU_shader_get_uniform(state.shader, "shuffle"), 4, 1, red); - immDrawPixelsTex( - &state, x1, y1, rectx, recty, GL_RED, GL_INT, GL_NEAREST, recti, zoomx, zoomy, NULL); + immDrawPixelsTex(&state, x1, y1, rectx, recty, GPU_R16F, false, rectf, zoomx, zoomy, NULL); - MEM_freeN(recti); + MEM_freeN(rectf); } static void sima_draw_zbuffloat_pixels(Scene *scene, @@ -526,8 +525,7 @@ static void sima_draw_zbuffloat_pixels(Scene *scene, GPU_shader_uniform_vector( state.shader, GPU_shader_get_uniform(state.shader, "shuffle"), 4, 1, red); - immDrawPixelsTex( - &state, x1, y1, rectx, recty, GL_RED, GL_FLOAT, GL_NEAREST, rectf, zoomx, zoomy, NULL); + immDrawPixelsTex(&state, x1, y1, rectx, recty, GL_R16F, false, rectf, zoomx, zoomy, NULL); MEM_freeN(rectf); } @@ -612,8 +610,7 @@ static void draw_image_buffer(const bContext *C, /* If RGBA display with color management */ if ((sima_flag & (SI_SHOW_R | SI_SHOW_G | SI_SHOW_B | SI_SHOW_ALPHA)) == 0) { - ED_draw_imbuf_ctx_clipping( - C, ibuf, x, y, GL_NEAREST, 0, 0, clip_max_x, clip_max_y, zoomx, zoomy); + ED_draw_imbuf_ctx_clipping(C, ibuf, x, y, false, 0, 0, clip_max_x, clip_max_y, zoomx, zoomy); } else { float shuffle[4] = {0.0f, 0.0f, 0.0f, 0.0f}; @@ -649,9 +646,8 @@ static void draw_image_buffer(const bContext *C, y, ibuf->x, ibuf->y, - GL_RGBA, - GL_UNSIGNED_BYTE, - GL_NEAREST, + GPU_RGBA8, + false, display_buffer, 0, 0, @@ -780,18 +776,8 @@ static void draw_image_paint_helpers( GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_COLOR); - immDrawPixelsTex(&state, - x, - y, - ibuf->x, - ibuf->y, - GL_RGBA, - GL_UNSIGNED_BYTE, - GL_NEAREST, - display_buffer, - zoomx, - zoomy, - col); + immDrawPixelsTex( + &state, x, y, ibuf->x, ibuf->y, GPU_RGBA8, false, display_buffer, zoomx, zoomy, col); GPU_blend(false); diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c index 695214bcc2f..4203eac16c4 100644 --- a/source/blender/editors/space_node/drawnode.c +++ b/source/blender/editors/space_node/drawnode.c @@ -3676,9 +3676,8 @@ void draw_nodespace_back_pix(const bContext *C, y, ibuf->x, ibuf->y, - GL_RGBA, - GL_UNSIGNED_BYTE, - GL_NEAREST, + GPU_RGBA8, + false, display_buffer, snode->zoom, snode->zoom, @@ -3691,12 +3690,12 @@ void draw_nodespace_back_pix(const bContext *C, GPU_blend_set_func_separate( GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); - ED_draw_imbuf_ctx(C, ibuf, x, y, GL_NEAREST, snode->zoom, snode->zoom); + ED_draw_imbuf_ctx(C, ibuf, x, y, false, snode->zoom, snode->zoom); GPU_blend(false); } else { - ED_draw_imbuf_ctx(C, ibuf, x, y, GL_NEAREST, snode->zoom, snode->zoom); + ED_draw_imbuf_ctx(C, ibuf, x, y, false, snode->zoom, snode->zoom); } if (cache_handle) { diff --git a/source/blender/editors/space_node/node_draw.c b/source/blender/editors/space_node/node_draw.c index 22b549cbd5d..52bd4d68649 100644 --- a/source/blender/editors/space_node/node_draw.c +++ b/source/blender/editors/space_node/node_draw.c @@ -947,9 +947,8 @@ static void node_draw_preview(bNodePreview *preview, rctf *prv) draw_rect.ymin, preview->xsize, preview->ysize, - GL_RGBA, - GL_UNSIGNED_BYTE, - GL_LINEAR, + GPU_RGBA8, + true, preview->rect, scale, scale, diff --git a/source/blender/editors/space_node/node_edit.c b/source/blender/editors/space_node/node_edit.c index 7af64e75656..3c927dbf25f 100644 --- a/source/blender/editors/space_node/node_edit.c +++ b/source/blender/editors/space_node/node_edit.c @@ -1697,6 +1697,8 @@ static int node_mute_exec(bContext *C, wmOperator *UNUSED(op)) } } + do_tag_update |= ED_node_is_simulation(snode); + snode_notify(C, snode); if (do_tag_update) { snode_dag_update(C, snode); @@ -1739,6 +1741,8 @@ static int node_delete_exec(bContext *C, wmOperator *UNUSED(op)) } } + do_tag_update |= ED_node_is_simulation(snode); + ntreeUpdateTree(CTX_data_main(C), snode->edittree); snode_notify(C, snode); diff --git a/source/blender/editors/space_node/node_relationships.c b/source/blender/editors/space_node/node_relationships.c index e5409271f7c..0a4607d2869 100644 --- a/source/blender/editors/space_node/node_relationships.c +++ b/source/blender/editors/space_node/node_relationships.c @@ -664,6 +664,8 @@ static void node_link_exit(bContext *C, wmOperator *op, bool apply_links) } ntree->is_updating = false; + do_tag_update |= ED_node_is_simulation(snode); + ntreeUpdateTree(bmain, ntree); snode_notify(C, snode); if (do_tag_update) { @@ -1064,6 +1066,8 @@ static int cut_links_exec(bContext *C, wmOperator *op) } } + do_tag_update |= ED_node_is_simulation(snode); + if (found) { ntreeUpdateTree(CTX_data_main(C), snode->edittree); snode_notify(C, snode); diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c index ce4fc0ff538..604d2f2a108 100644 --- a/source/blender/editors/space_sequencer/sequencer_edit.c +++ b/source/blender/editors/space_sequencer/sequencer_edit.c @@ -666,74 +666,6 @@ int seq_effect_find_selected(Scene *scene, /** \name Delete Utilities * \{ */ -static Sequence *del_seq_find_replace_recurs(Scene *scene, Sequence *seq) -{ - Sequence *seq1, *seq2, *seq3; - - /* Try to find a replacement input sequence, and flag for later deletion if - * no replacement can be found. */ - - if (!seq) { - return NULL; - } - if (!(seq->type & SEQ_TYPE_EFFECT)) { - return ((seq->flag & SELECT) ? NULL : seq); - } - if (!(seq->flag & SELECT)) { - /* Try to find replacement for effect inputs. */ - seq1 = del_seq_find_replace_recurs(scene, seq->seq1); - seq2 = del_seq_find_replace_recurs(scene, seq->seq2); - seq3 = del_seq_find_replace_recurs(scene, seq->seq3); - - if (seq1 == seq->seq1 && seq2 == seq->seq2 && seq3 == seq->seq3) { - /* Pass. */ - } - else if (seq1 || seq2 || seq3) { - seq->seq1 = (seq1) ? seq1 : (seq2) ? seq2 : seq3; - seq->seq2 = (seq2) ? seq2 : (seq1) ? seq1 : seq3; - seq->seq3 = (seq3) ? seq3 : (seq1) ? seq1 : seq2; - - BKE_sequencer_update_changed_seq_and_deps(scene, seq, 1, 1); - } - else { - seq->flag |= SELECT; /* Mark for delete. */ - } - } - - if (seq->flag & SELECT) { - if ((seq1 = del_seq_find_replace_recurs(scene, seq->seq1))) { - return seq1; - } - if ((seq2 = del_seq_find_replace_recurs(scene, seq->seq2))) { - return seq2; - } - if ((seq3 = del_seq_find_replace_recurs(scene, seq->seq3))) { - return seq3; - } - return NULL; - } - return seq; -} - -static void del_seq_clear_modifiers_recurs(Scene *scene, Sequence *deleting_sequence) -{ - Editing *ed = BKE_sequencer_editing_get(scene, false); - Sequence *current_sequence; - - SEQP_BEGIN (ed, current_sequence) { - if (!(current_sequence->flag & SELECT) && current_sequence != deleting_sequence) { - SequenceModifierData *smd; - - for (smd = current_sequence->modifiers.first; smd; smd = smd->next) { - if (smd->mask_sequence == deleting_sequence) { - smd->mask_sequence = NULL; - } - } - } - } - SEQ_END; -} - static void recurs_del_seq_flag(Scene *scene, ListBase *lb, short flag, short deleteall) { Sequence *seq, *seqn; @@ -2582,61 +2514,18 @@ static int sequencer_delete_exec(bContext *C, wmOperator *UNUSED(op)) Scene *scene = CTX_data_scene(C); Editing *ed = BKE_sequencer_editing_get(scene, false); Sequence *seq; - MetaStack *ms; - bool nothing_selected = true; - - BKE_sequencer_prefetch_stop(scene); - - seq = BKE_sequencer_active_get(scene); - if (seq && seq->flag & SELECT) { /* Avoid a loop since this is likely to be selected. */ - nothing_selected = false; - } - else { - for (seq = ed->seqbasep->first; seq; seq = seq->next) { - if (seq->flag & SELECT) { - nothing_selected = false; - break; - } - } - } - - if (nothing_selected) { - return OPERATOR_FINISHED; - } - /* For effects and modifiers, try to find a replacement input. */ - for (seq = ed->seqbasep->first; seq; seq = seq->next) { - if (!(seq->flag & SELECT)) { - if ((seq->type & SEQ_TYPE_EFFECT)) { - del_seq_find_replace_recurs(scene, seq); - } - } - else { - del_seq_clear_modifiers_recurs(scene, seq); + SEQP_BEGIN (scene->ed, seq) { + if (seq->flag & SELECT) { + BKE_sequencer_flag_for_removal(scene, ed->seqbasep, seq); } } - - /* Delete all selected strips. */ - recurs_del_seq_flag(scene, ed->seqbasep, SELECT, 0); - - /* Update lengths, etc. */ - seq = ed->seqbasep->first; - while (seq) { - BKE_sequence_calc(scene, seq); - seq = seq->next; - } - - /* Free parent metas. */ - ms = ed->metastack.last; - while (ms) { - BKE_sequence_calc(scene, ms->parseq); - ms = ms->prev; - } + SEQ_END; + BKE_sequencer_remove_flagged_sequences(scene, ed->seqbasep); DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS); DEG_relations_tag_update(bmain); WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); - return OPERATOR_FINISHED; } diff --git a/source/blender/editors/space_view3d/view3d_buttons.c b/source/blender/editors/space_view3d/view3d_buttons.c index 2e170126574..d78c58c0c64 100644 --- a/source/blender/editors/space_view3d/view3d_buttons.c +++ b/source/blender/editors/space_view3d/view3d_buttons.c @@ -1474,6 +1474,7 @@ static void v3d_editmetaball_buts(uiLayout *layout, Object *ob) uiLayout *col; if (!mball || !(mball->lastelem)) { + uiItemL(layout, IFACE_("Nothing selected"), ICON_NONE); return; } diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c index cc5f7deb418..84b3578e978 100644 --- a/source/blender/editors/space_view3d/view3d_draw.c +++ b/source/blender/editors/space_view3d/view3d_draw.c @@ -1954,10 +1954,10 @@ ImBuf *ED_view3d_draw_offscreen_imbuf(Depsgraph *depsgraph, NULL); if (ibuf->rect_float) { - GPU_offscreen_read_pixels(ofs, GL_FLOAT, ibuf->rect_float); + GPU_offscreen_read_pixels(ofs, GPU_DATA_FLOAT, ibuf->rect_float); } else if (ibuf->rect) { - GPU_offscreen_read_pixels(ofs, GL_UNSIGNED_BYTE, ibuf->rect); + GPU_offscreen_read_pixels(ofs, GPU_DATA_UNSIGNED_BYTE, ibuf->rect); } /* unbind */ diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c index fc763d91e3d..2c8ea396a99 100644 --- a/source/blender/editors/space_view3d/view3d_select.c +++ b/source/blender/editors/space_view3d/view3d_select.c @@ -97,7 +97,6 @@ #include "UI_interface.h" -#include "GPU_glew.h" #include "GPU_matrix.h" #include "DEG_depsgraph.h" diff --git a/source/blender/editors/space_view3d/view3d_view.c b/source/blender/editors/space_view3d/view3d_view.c index 66efa5b5de3..ff9673a4262 100644 --- a/source/blender/editors/space_view3d/view3d_view.c +++ b/source/blender/editors/space_view3d/view3d_view.c @@ -50,7 +50,6 @@ #include "UI_resources.h" -#include "GPU_glew.h" #include "GPU_matrix.h" #include "GPU_select.h" #include "GPU_state.h" diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h index 2bda04ad811..bbd18b4bdaf 100644 --- a/source/blender/editors/transform/transform.h +++ b/source/blender/editors/transform/transform.h @@ -81,6 +81,7 @@ typedef struct TransSnap { bool snap_self; bool peel; bool snap_spatial_grid; + bool use_backface_culling; char status; /* Snapped Element Type (currently for objects only). */ char snapElem; diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c index 2943c3cb8ea..a700dd320b7 100644 --- a/source/blender/editors/transform/transform_snap.c +++ b/source/blender/editors/transform/transform_snap.c @@ -37,6 +37,7 @@ #include "BKE_editmesh.h" #include "BKE_layer.h" #include "BKE_object.h" +#include "BKE_scene.h" #include "BKE_sequencer.h" #include "RNA_access.h" @@ -100,6 +101,24 @@ int BIF_snappingSupported(Object *obedit) } #endif +static bool snap_use_backface_culling(const TransInfo *t) +{ + BLI_assert(t->spacetype == SPACE_VIEW3D); + View3D *v3d = t->view; + if ((v3d->shading.type == OB_SOLID) && (v3d->shading.flag & V3D_SHADING_BACKFACE_CULLING)) { + return true; + } + if (v3d->shading.type == OB_RENDER && + (t->scene->display.shading.flag & V3D_SHADING_BACKFACE_CULLING) && + BKE_scene_uses_blender_workbench(t->scene)) { + return true; + } + if (t->settings->snap_flag & SCE_SNAP_BACKFACE_CULLING) { + return true; + } + return false; +} + bool validSnap(const TransInfo *t) { return (t->tsnap.status & (POINT_INIT | TARGET_INIT)) == (POINT_INIT | TARGET_INIT) || @@ -315,8 +334,7 @@ void applyProject(TransInfo *t) .snap_select = t->tsnap.modeSelect, .use_object_edit_cage = (t->flag & T_EDIT) != 0, .use_occlusion_test = false, - .use_backface_culling = (t->scene->toolsettings->snap_flag & - SCE_SNAP_BACKFACE_CULLING) != 0, + .use_backface_culling = t->tsnap.use_backface_culling, }, mval_fl, NULL, @@ -601,6 +619,7 @@ static void initSnappingMode(TransInfo *t) if (t->spacetype == SPACE_VIEW3D) { if (t->tsnap.object_context == NULL) { + t->tsnap.use_backface_culling = snap_use_backface_culling(t); t->tsnap.object_context = ED_transform_snap_object_context_create_view3d( t->scene, 0, t->region, t->view); @@ -1120,13 +1139,12 @@ short snapObjectsTransform( return ED_transform_snap_object_project_view3d_ex( t->tsnap.object_context, t->depsgraph, - t->scene->toolsettings->snap_mode, + t->settings->snap_mode, &(const struct SnapObjectParams){ .snap_select = t->tsnap.modeSelect, .use_object_edit_cage = (t->flag & T_EDIT) != 0, - .use_occlusion_test = t->scene->toolsettings->snap_mode != SCE_SNAP_MODE_FACE, - .use_backface_culling = (t->scene->toolsettings->snap_flag & - SCE_SNAP_BACKFACE_CULLING) != 0, + .use_occlusion_test = t->settings->snap_mode != SCE_SNAP_MODE_FACE, + .use_backface_culling = t->tsnap.use_backface_culling, }, mval, target, diff --git a/source/blender/editors/util/numinput.c b/source/blender/editors/util/numinput.c index 3c747a29361..384da6fb931 100644 --- a/source/blender/editors/util/numinput.c +++ b/source/blender/editors/util/numinput.c @@ -26,6 +26,8 @@ #include "BLI_string_utf8.h" #include "BLI_utildefines.h" +#include "BLT_translation.h" + #include "BKE_context.h" #include "BKE_scene.h" #include "BKE_unit.h" @@ -277,8 +279,12 @@ static bool editstr_insert_at_cursor(NumInput *n, const char *buf, const int buf return true; } -bool user_string_to_number( - bContext *C, const char *str, const UnitSettings *unit, int type, double *r_value) +bool user_string_to_number(bContext *C, + const char *str, + const UnitSettings *unit, + int type, + const char *error_prefix, + double *r_value) { #ifdef WITH_PYTHON double unit_scale = BKE_scene_unit_scale(unit, type, 1.0); @@ -288,10 +294,10 @@ bool user_string_to_number( bUnit_ReplaceString( str_unit_convert, sizeof(str_unit_convert), str, unit_scale, unit->system, type); - return BPY_execute_string_as_number(C, NULL, str_unit_convert, true, r_value); + return BPY_execute_string_as_number(C, NULL, str_unit_convert, error_prefix, r_value); } - int success = BPY_execute_string_as_number(C, NULL, str, true, r_value); + int success = BPY_execute_string_as_number(C, NULL, str, error_prefix, r_value); *r_value *= bUnit_PreferredInputUnitScalar(unit, type); *r_value /= unit_scale; return success; @@ -573,7 +579,8 @@ bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event) Scene *sce = CTX_data_scene(C); double val; - int success = user_string_to_number(C, n->str, &sce->unit, n->unit_type[idx], &val); + int success = user_string_to_number( + C, n->str, &sce->unit, n->unit_type[idx], IFACE_("Numeric input evaluation"), &val); if (success) { n->val[idx] = (float)val; diff --git a/source/blender/editors/uvedit/uvedit_intern.h b/source/blender/editors/uvedit/uvedit_intern.h index d5e7dd08fd1..d4f284a1619 100644 --- a/source/blender/editors/uvedit/uvedit_intern.h +++ b/source/blender/editors/uvedit/uvedit_intern.h @@ -108,6 +108,7 @@ void UV_OT_sphere_project(struct wmOperatorType *ot); void UV_OT_unwrap(struct wmOperatorType *ot); void UV_OT_rip(struct wmOperatorType *ot); void UV_OT_stitch(struct wmOperatorType *ot); +void UV_OT_smart_project(struct wmOperatorType *ot); /* uvedit_path.c */ void UV_OT_shortest_path_pick(struct wmOperatorType *ot); diff --git a/source/blender/editors/uvedit/uvedit_ops.c b/source/blender/editors/uvedit/uvedit_ops.c index e2368ae6ba8..ad98321c458 100644 --- a/source/blender/editors/uvedit/uvedit_ops.c +++ b/source/blender/editors/uvedit/uvedit_ops.c @@ -2094,6 +2094,7 @@ void ED_operatortypes_uvedit(void) WM_operatortype_append(UV_OT_reset); WM_operatortype_append(UV_OT_sphere_project); WM_operatortype_append(UV_OT_unwrap); + WM_operatortype_append(UV_OT_smart_project); WM_operatortype_append(UV_OT_reveal); WM_operatortype_append(UV_OT_hide); diff --git a/source/blender/editors/uvedit/uvedit_unwrap_ops.c b/source/blender/editors/uvedit/uvedit_unwrap_ops.c index 6fcfb0e0bfc..2f30ef1caa6 100644 --- a/source/blender/editors/uvedit/uvedit_unwrap_ops.c +++ b/source/blender/editors/uvedit/uvedit_unwrap_ops.c @@ -35,7 +35,10 @@ #include "DNA_scene_types.h" #include "BLI_alloca.h" +#include "BLI_array.h" +#include "BLI_linklist.h" #include "BLI_math.h" +#include "BLI_memarena.h" #include "BLI_string.h" #include "BLI_utildefines.h" #include "BLI_uvproject.h" @@ -147,7 +150,13 @@ typedef struct UnwrapOptions { /** Connectivity based on UV coordinates instead of seams. */ bool topology_from_uvs; /** Only affect selected faces. */ - bool only_selected; + bool only_selected_faces; + /** + * Only affect selected UV's. + * \note Disable this for operations that don't run in the image-window. + * Unwrapping from the 3D view for example, where only 'only_selected_faces' should be used. + */ + bool only_selected_uvs; /** Fill holes to better preserve shape. */ bool fill_holes; /** Correct for mapped image texture aspect ratio. */ @@ -299,7 +308,7 @@ static ParamHandle *construct_param_handle(const Scene *scene, BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, i) { if ((BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) || - (options->only_selected && BM_elem_flag_test(efa, BM_ELEM_SELECT) == 0)) { + (options->only_selected_faces && BM_elem_flag_test(efa, BM_ELEM_SELECT) == 0)) { continue; } @@ -307,10 +316,12 @@ static ParamHandle *construct_param_handle(const Scene *scene, bool is_loopsel = false; BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) { - is_loopsel = true; - break; + if (options->only_selected_uvs && + (uvedit_uv_select_test(scene, l, cd_loop_uv_offset) == false)) { + continue; } + is_loopsel = true; + break; } if (is_loopsel == false) { continue; @@ -382,7 +393,7 @@ static ParamHandle *construct_param_handle_multi(const Scene *scene, BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, i) { if ((BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) || - (options->only_selected && BM_elem_flag_test(efa, BM_ELEM_SELECT) == 0)) { + (options->only_selected_faces && BM_elem_flag_test(efa, BM_ELEM_SELECT) == 0)) { continue; } @@ -390,10 +401,12 @@ static ParamHandle *construct_param_handle_multi(const Scene *scene, bool is_loopsel = false; BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) { - is_loopsel = true; - break; + if (options->only_selected_uvs && + (uvedit_uv_select_test(scene, l, cd_loop_uv_offset) == false)) { + continue; } + is_loopsel = true; + break; } if (is_loopsel == false) { continue; @@ -571,7 +584,7 @@ static ParamHandle *construct_param_handle_subsurfed(const Scene *scene, } else { if (BM_elem_flag_test(origFace, BM_ELEM_HIDDEN) || - (options->only_selected && !BM_elem_flag_test(origFace, BM_ELEM_SELECT))) { + (options->only_selected_faces && !BM_elem_flag_test(origFace, BM_ELEM_SELECT))) { continue; } } @@ -671,7 +684,8 @@ static bool minimize_stretch_init(bContext *C, wmOperator *op) const UnwrapOptions options = { .topology_from_uvs = true, .fill_holes = RNA_boolean_get(op->ptr, "fill_holes"), - .only_selected = true, + .only_selected_faces = true, + .only_selected_uvs = true, .correct_aspect = true, }; @@ -933,7 +947,8 @@ static void uvedit_pack_islands(const Scene *scene, Object *ob, BMesh *bm) { const UnwrapOptions options = { .topology_from_uvs = true, - .only_selected = false, + .only_selected_faces = false, + .only_selected_uvs = true, .fill_holes = false, .correct_aspect = false, }; @@ -975,7 +990,8 @@ static int pack_islands_exec(bContext *C, wmOperator *op) const UnwrapOptions options = { .topology_from_uvs = true, - .only_selected = true, + .only_selected_faces = true, + .only_selected_uvs = true, .fill_holes = false, .correct_aspect = true, }; @@ -1040,7 +1056,8 @@ static int average_islands_scale_exec(bContext *C, wmOperator *UNUSED(op)) const UnwrapOptions options = { .topology_from_uvs = true, - .only_selected = true, + .only_selected_faces = true, + .only_selected_uvs = true, .fill_holes = false, .correct_aspect = true, }; @@ -1114,7 +1131,8 @@ void ED_uvedit_live_unwrap_begin(Scene *scene, Object *obedit) const UnwrapOptions options = { .topology_from_uvs = false, - .only_selected = false, + .only_selected_faces = false, + .only_selected_uvs = true, .fill_holes = (scene->toolsettings->uvcalc_flag & UVCALC_FILLHOLES) != 0, .correct_aspect = (scene->toolsettings->uvcalc_flag & UVCALC_NO_ASPECT_CORRECT) == 0, }; @@ -1471,18 +1489,21 @@ static void correct_uv_aspect(Object *ob, BMEditMesh *em) /** \name UV Map Clip & Correct * \{ */ -static void uv_map_clip_correct_properties(wmOperatorType *ot) +static void uv_map_clip_correct_properties_ex(wmOperatorType *ot, bool clip_to_bounds) { RNA_def_boolean(ot->srna, "correct_aspect", 1, "Correct Aspect", "Map UVs taking image aspect ratio into account"); - RNA_def_boolean(ot->srna, - "clip_to_bounds", - 0, - "Clip to Bounds", - "Clip UV coordinates to bounds after unwrapping"); + /* Optional, since not all unwrapping types need to be clipped. */ + if (clip_to_bounds) { + RNA_def_boolean(ot->srna, + "clip_to_bounds", + 0, + "Clip to Bounds", + "Clip UV coordinates to bounds after unwrapping"); + } RNA_def_boolean(ot->srna, "scale_to_bounds", 0, @@ -1490,6 +1511,11 @@ static void uv_map_clip_correct_properties(wmOperatorType *ot) "Scale UV coordinates to bounds after unwrapping"); } +static void uv_map_clip_correct_properties(wmOperatorType *ot) +{ + uv_map_clip_correct_properties_ex(ot, true); +} + static void uv_map_clip_correct_multi(Object **objects, uint objects_len, wmOperator *op) { BMFace *efa; @@ -1498,7 +1524,8 @@ static void uv_map_clip_correct_multi(Object **objects, uint objects_len, wmOper MLoopUV *luv; float dx, dy, min[2], max[2]; const bool correct_aspect = RNA_boolean_get(op->ptr, "correct_aspect"); - const bool clip_to_bounds = RNA_boolean_get(op->ptr, "clip_to_bounds"); + const bool clip_to_bounds = (RNA_struct_find_property(op->ptr, "clip_to_bounds") && + RNA_boolean_get(op->ptr, "clip_to_bounds")); const bool scale_to_bounds = RNA_boolean_get(op->ptr, "scale_to_bounds"); INIT_MINMAX2(min, max); @@ -1635,7 +1662,8 @@ void ED_uvedit_live_unwrap(const Scene *scene, Object **objects, int objects_len if (scene->toolsettings->edge_mode_live_unwrap) { const UnwrapOptions options = { .topology_from_uvs = false, - .only_selected = false, + .only_selected_faces = false, + .only_selected_uvs = true, .fill_holes = (scene->toolsettings->uvcalc_flag & UVCALC_FILLHOLES) != 0, .correct_aspect = (scene->toolsettings->uvcalc_flag & UVCALC_NO_ASPECT_CORRECT) == 0, }; @@ -1670,7 +1698,8 @@ static int unwrap_exec(bContext *C, wmOperator *op) const UnwrapOptions options = { .topology_from_uvs = false, - .only_selected = true, + .only_selected_faces = true, + .only_selected_uvs = true, .fill_holes = RNA_boolean_get(op->ptr, "fill_holes"), .correct_aspect = RNA_boolean_get(op->ptr, "correct_aspect"), }; @@ -1829,6 +1858,365 @@ void UV_OT_unwrap(wmOperatorType *ot) /** \} */ /* -------------------------------------------------------------------- */ +/** \name Smart UV Project Operator + * \{ */ + +/* Ignore all areas below this, as the UV's get zeroed. */ +static const float smart_uv_project_area_ignore = 1e-12f; + +typedef struct ThickFace { + float area; + BMFace *efa; +} ThickFace; + +static int smart_uv_project_thickface_area_cmp_fn(const void *tf_a_p, const void *tf_b_p) +{ + + const ThickFace *tf_a = (ThickFace *)tf_a_p; + const ThickFace *tf_b = (ThickFace *)tf_b_p; + + /* Ignore the area of small faces. + * Also, order checks so `!isfinite(...)` values are counted as zero area. */ + if (!((tf_a->area > smart_uv_project_area_ignore) || + (tf_b->area > smart_uv_project_area_ignore))) { + return 0; + } + + if (tf_a->area < tf_b->area) { + return 1; + } + else if (tf_a->area > tf_b->area) { + return -1; + } + else { + return 0; + } +} + +static uint smart_uv_project_calculate_project_normals(const ThickFace *thick_faces, + const uint thick_faces_len, + BMesh *bm, + const float project_angle_limit_half_cos, + const float project_angle_limit_cos, + const float area_weight, + float (**r_project_normal_array)[3]) +{ + if (UNLIKELY(thick_faces_len == 0)) { + *r_project_normal_array = NULL; + return 0; + } + + const float *project_normal = thick_faces[0].efa->no; + + const ThickFace **project_thick_faces = NULL; + BLI_array_declare(project_thick_faces); + + float(*project_normal_array)[3] = NULL; + BLI_array_declare(project_normal_array); + + BM_mesh_elem_hflag_disable_all(bm, BM_FACE, BM_ELEM_TAG, false); + + while (true) { + for (int f_index = thick_faces_len - 1; f_index >= 0; f_index--) { + if (BM_elem_flag_test(thick_faces[f_index].efa, BM_ELEM_TAG)) { + continue; + } + + if (dot_v3v3(thick_faces[f_index].efa->no, project_normal) > project_angle_limit_half_cos) { + BLI_array_append(project_thick_faces, &thick_faces[f_index]); + BM_elem_flag_set(thick_faces[f_index].efa, BM_ELEM_TAG, true); + } + } + + float average_normal[3] = {0.0f, 0.0f, 0.0f}; + + if (area_weight <= 0.0f) { + for (int f_proj_index = 0; f_proj_index < BLI_array_len(project_thick_faces); + f_proj_index++) { + const ThickFace *tf = project_thick_faces[f_proj_index]; + add_v3_v3(average_normal, tf->efa->no); + } + } + else if (area_weight >= 1.0f) { + for (int f_proj_index = 0; f_proj_index < BLI_array_len(project_thick_faces); + f_proj_index++) { + const ThickFace *tf = project_thick_faces[f_proj_index]; + madd_v3_v3fl(average_normal, tf->efa->no, tf->area); + } + } + else { + for (int f_proj_index = 0; f_proj_index < BLI_array_len(project_thick_faces); + f_proj_index++) { + const ThickFace *tf = project_thick_faces[f_proj_index]; + const float area_blend = (tf->area * area_weight) + (1.0f - area_weight); + madd_v3_v3fl(average_normal, tf->efa->no, area_blend); + } + } + + /* Avoid NAN. */ + if (normalize_v3(average_normal) != 0.0f) { + float(*normal)[3] = BLI_array_append_ret(project_normal_array); + copy_v3_v3(*normal, average_normal); + } + + /* Find the most unique angle that points away from other normals. */ + float anble_best = 1.0f; + uint angle_best_index = 0; + + for (int f_index = thick_faces_len - 1; f_index >= 0; f_index--) { + if (BM_elem_flag_test(thick_faces[f_index].efa, BM_ELEM_TAG)) { + continue; + } + + float angle_test = -1.0f; + for (int p_index = 0; p_index < BLI_array_len(project_normal_array); p_index++) { + angle_test = max_ff(angle_test, + dot_v3v3(project_normal_array[p_index], thick_faces[f_index].efa->no)); + } + + if (angle_test < anble_best) { + anble_best = angle_test; + angle_best_index = f_index; + } + } + + if (anble_best < project_angle_limit_cos) { + project_normal = thick_faces[angle_best_index].efa->no; + BLI_array_clear(project_thick_faces); + BLI_array_append(project_thick_faces, &thick_faces[angle_best_index]); + BM_elem_flag_enable(thick_faces[angle_best_index].efa, BM_ELEM_TAG); + } + else { + if (BLI_array_len(project_normal_array) >= 1) { + break; + } + } + } + + BLI_array_free(project_thick_faces); + BM_mesh_elem_hflag_disable_all(bm, BM_FACE, BM_ELEM_TAG, false); + + *r_project_normal_array = project_normal_array; + return BLI_array_len(project_normal_array); +} + +static int smart_project_exec(bContext *C, wmOperator *op) +{ + Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + + /* May be NULL. */ + View3D *v3d = CTX_wm_view3d(C); + + const float project_angle_limit = RNA_float_get(op->ptr, "angle_limit"); + const float island_margin = RNA_float_get(op->ptr, "island_margin"); + const float area_weight = RNA_float_get(op->ptr, "area_weight"); + + const float project_angle_limit_cos = cosf(project_angle_limit); + const float project_angle_limit_half_cos = cosf(project_angle_limit / 2); + + /* Memory arena for list links (cleared for each object). */ + MemArena *arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); + + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs( + view_layer, v3d, &objects_len); + + Object **objects_changed = MEM_mallocN(sizeof(*objects_changed) * objects_len, __func__); + uint object_changed_len = 0; + + BMFace *efa; + BMIter iter; + uint ob_index; + + for (ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + bool changed = false; + + const uint cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); + ThickFace *thick_faces = MEM_mallocN(sizeof(*thick_faces) * em->bm->totface, __func__); + + uint thick_faces_len = 0; + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + if (!BM_elem_flag_test(efa, BM_ELEM_SELECT)) { + continue; + } + thick_faces[thick_faces_len].area = BM_face_calc_area(efa); + thick_faces[thick_faces_len].efa = efa; + thick_faces_len++; + } + + qsort(thick_faces, thick_faces_len, sizeof(ThickFace), smart_uv_project_thickface_area_cmp_fn); + + /* Remove all zero area faces. */ + while ((thick_faces_len > 0) && + !(thick_faces[thick_faces_len - 1].area > smart_uv_project_area_ignore)) { + + /* Zero UV's so they don't overlap with other faces being unwrapped. */ + BMIter liter; + BMLoop *l; + BM_ITER_ELEM (l, &liter, thick_faces[thick_faces_len - 1].efa, BM_LOOPS_OF_FACE) { + MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + zero_v2(luv->uv); + changed = true; + } + + thick_faces_len -= 1; + } + + float(*project_normal_array)[3] = NULL; + int project_normals_len = smart_uv_project_calculate_project_normals( + thick_faces, + thick_faces_len, + em->bm, + project_angle_limit_half_cos, + project_angle_limit_cos, + area_weight, + &project_normal_array); + + if (project_normals_len == 0) { + MEM_freeN(thick_faces); + BLI_assert(project_normal_array == NULL); + continue; + } + + /* After finding projection vectors, we find the uv positions. */ + LinkNode **thickface_project_groups = MEM_callocN( + sizeof(*thickface_project_groups) * project_normals_len, __func__); + + BLI_memarena_clear(arena); + + for (int f_index = thick_faces_len - 1; f_index >= 0; f_index--) { + const float *f_normal = thick_faces[f_index].efa->no; + + float angle_best = dot_v3v3(f_normal, project_normal_array[0]); + uint angle_best_index = 0; + + for (int p_index = 1; p_index < project_normals_len; p_index++) { + const float angle_test = dot_v3v3(f_normal, project_normal_array[p_index]); + if (angle_test > angle_best) { + angle_best = angle_test; + angle_best_index = p_index; + } + } + + BLI_linklist_prepend_arena( + &thickface_project_groups[angle_best_index], &thick_faces[f_index], arena); + } + + for (int p_index = 0; p_index < project_normals_len; p_index++) { + if (thickface_project_groups[p_index] == NULL) { + continue; + } + + float axis_mat[3][3]; + axis_dominant_v3_to_m3_negate(axis_mat, project_normal_array[p_index]); + + for (LinkNode *list = thickface_project_groups[p_index]; list; list = list->next) { + ThickFace *tf = list->link; + BMIter liter; + BMLoop *l; + BM_ITER_ELEM (l, &liter, tf->efa, BM_LOOPS_OF_FACE) { + MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + mul_v2_m3v3(luv->uv, axis_mat, l->v->co); + } + changed = true; + } + } + + MEM_freeN(thick_faces); + MEM_freeN(project_normal_array); + + /* No need to free the lists in 'thickface_project_groups' values as the 'arena' is used. */ + MEM_freeN(thickface_project_groups); + + if (changed) { + objects_changed[object_changed_len] = objects[ob_index]; + object_changed_len += 1; + } + } + + BLI_memarena_free(arena); + + MEM_freeN(objects); + + /* Pack islands & Stretch to UV bounds */ + if (object_changed_len > 0) { + + scene->toolsettings->uvcalc_margin = island_margin; + const UnwrapOptions options = { + .topology_from_uvs = true, + .only_selected_faces = true, + .only_selected_uvs = false, + .fill_holes = true, + .correct_aspect = false, + }; + + /* Depsgraph refresh functions are called here. */ + uvedit_pack_islands_multi(scene, objects_changed, object_changed_len, &options, true, false); + uv_map_clip_correct_multi(objects_changed, object_changed_len, op); + } + + MEM_freeN(objects_changed); + + return OPERATOR_FINISHED; +} + +void UV_OT_smart_project(wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Smart UV Project"; + ot->idname = "UV_OT_smart_project"; + ot->description = "Projection unwraps the selected faces of mesh objects"; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* api callbacks */ + ot->exec = smart_project_exec; + ot->poll = ED_operator_uvmap; + ot->invoke = WM_operator_props_popup_confirm; + + /* properties */ + prop = RNA_def_float_rotation(ot->srna, + "angle_limit", + 0, + NULL, + DEG2RADF(0.0f), + DEG2RADF(90.0f), + "Angle Limit", + "Lower for more projection groups, higher for less distortion", + DEG2RADF(0.0f), + DEG2RADF(89.0f)); + RNA_def_property_float_default(prop, DEG2RADF(66.0f)); + + RNA_def_float(ot->srna, + "island_margin", + 0.0f, + 0.0f, + 1.0f, + "Island Margin", + "Margin to reduce bleed from adjacent islands", + 0.0f, + 1.0f); + RNA_def_float(ot->srna, + "area_weight", + 0.0f, + 0.0f, + 1.0f, + "Area Weight", + "Weight projections vector by faces with larger areas", + 0.0f, + 1.0f); + + uv_map_clip_correct_properties_ex(ot, false); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Project UV From View Operator * \{ */ diff --git a/source/blender/functions/CMakeLists.txt b/source/blender/functions/CMakeLists.txt index 2686275e898..ad29dbe6668 100644 --- a/source/blender/functions/CMakeLists.txt +++ b/source/blender/functions/CMakeLists.txt @@ -57,3 +57,20 @@ set(LIB ) blender_add_lib(bf_functions "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") + +if(WITH_GTESTS) + set(TEST_SRC + tests/FN_array_spans_test.cc + tests/FN_attributes_ref_test.cc + tests/FN_cpp_type_test.cc + tests/FN_generic_vector_array_test.cc + tests/FN_multi_function_network_test.cc + tests/FN_multi_function_test.cc + tests/FN_spans_test.cc + ) + set (TEST_LIB + bf_functions + ) + include(GTestTesting) + blender_add_test_lib(bf_functions_tests "${TEST_SRC}" "${INC};${TEST_INC}" "${INC_SYS}" "${LIB};${TEST_LIB}") +endif() diff --git a/source/blender/functions/FN_array_spans.hh b/source/blender/functions/FN_array_spans.hh index c362fef3630..5f976711e06 100644 --- a/source/blender/functions/FN_array_spans.hh +++ b/source/blender/functions/FN_array_spans.hh @@ -158,7 +158,7 @@ class GVArraySpan : public VArraySpanBase<void> { this->type_ = &array.type(); this->virtual_size_ = virtual_size; this->category_ = VArraySpanCategory::SingleArray; - this->data_.single_array.start = array.buffer(); + this->data_.single_array.start = array.data(); this->data_.single_array.size = array.size(); } diff --git a/source/blender/functions/FN_attributes_ref.hh b/source/blender/functions/FN_attributes_ref.hh index ed14676731e..c694f11b7a7 100644 --- a/source/blender/functions/FN_attributes_ref.hh +++ b/source/blender/functions/FN_attributes_ref.hh @@ -50,12 +50,12 @@ class AttributesInfoBuilder : NonCopyable, NonMovable { AttributesInfoBuilder() = default; ~AttributesInfoBuilder(); - template<typename T> void add(StringRef name, const T &default_value) + template<typename T> bool add(StringRef name, const T &default_value) { - this->add(name, CPPType::get<T>(), (const void *)&default_value); + return this->add(name, CPPType::get<T>(), (const void *)&default_value); } - void add(StringRef name, const CPPType &type, const void *default_value = nullptr); + bool add(StringRef name, const CPPType &type, const void *default_value = nullptr); }; /** diff --git a/source/blender/functions/FN_cpp_type.hh b/source/blender/functions/FN_cpp_type.hh index 594890e353a..531a9073784 100644 --- a/source/blender/functions/FN_cpp_type.hh +++ b/source/blender/functions/FN_cpp_type.hh @@ -371,7 +371,7 @@ class CPPType : NonCopyable, NonMovable { void copy_to_initialized_n(const void *src, void *dst, int64_t n) const { - BLI_assert(src != dst); + BLI_assert(n == 0 || src != dst); BLI_assert(n == 0 || this->pointer_can_point_to_instance(src)); BLI_assert(n == 0 || this->pointer_can_point_to_instance(dst)); @@ -380,7 +380,7 @@ class CPPType : NonCopyable, NonMovable { void copy_to_initialized_indices(const void *src, void *dst, IndexMask mask) const { - BLI_assert(src != dst); + BLI_assert(mask.size() == 0 || src != dst); BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(src)); BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(dst)); @@ -406,7 +406,7 @@ class CPPType : NonCopyable, NonMovable { void copy_to_uninitialized_n(const void *src, void *dst, int64_t n) const { - BLI_assert(src != dst); + BLI_assert(n == 0 || src != dst); BLI_assert(n == 0 || this->pointer_can_point_to_instance(src)); BLI_assert(n == 0 || this->pointer_can_point_to_instance(dst)); @@ -415,7 +415,7 @@ class CPPType : NonCopyable, NonMovable { void copy_to_uninitialized_indices(const void *src, void *dst, IndexMask mask) const { - BLI_assert(src != dst); + BLI_assert(mask.size() == 0 || src != dst); BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(src)); BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(dst)); @@ -441,7 +441,7 @@ class CPPType : NonCopyable, NonMovable { void relocate_to_initialized_n(void *src, void *dst, int64_t n) const { - BLI_assert(src != dst); + BLI_assert(n == 0 || src != dst); BLI_assert(n == 0 || this->pointer_can_point_to_instance(src)); BLI_assert(n == 0 || this->pointer_can_point_to_instance(dst)); @@ -450,7 +450,7 @@ class CPPType : NonCopyable, NonMovable { void relocate_to_initialized_indices(void *src, void *dst, IndexMask mask) const { - BLI_assert(src != dst); + BLI_assert(mask.size() == 0 || src != dst); BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(src)); BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(dst)); @@ -476,7 +476,7 @@ class CPPType : NonCopyable, NonMovable { void relocate_to_uninitialized_n(void *src, void *dst, int64_t n) const { - BLI_assert(src != dst); + BLI_assert(n == 0 || src != dst); BLI_assert(n == 0 || this->pointer_can_point_to_instance(src)); BLI_assert(n == 0 || this->pointer_can_point_to_instance(dst)); @@ -485,7 +485,7 @@ class CPPType : NonCopyable, NonMovable { void relocate_to_uninitialized_indices(void *src, void *dst, IndexMask mask) const { - BLI_assert(src != dst); + BLI_assert(mask.size() == 0 || src != dst); BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(src)); BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(dst)); diff --git a/source/blender/functions/FN_multi_function_params.hh b/source/blender/functions/FN_multi_function_params.hh index 93d7b47af83..ac4dca33cf0 100644 --- a/source/blender/functions/FN_multi_function_params.hh +++ b/source/blender/functions/FN_multi_function_params.hh @@ -50,52 +50,56 @@ class MFParamsBuilder { MFParamsBuilder(const class MultiFunction &fn, int64_t min_array_size); - template<typename T> void add_readonly_single_input(const T *value) + template<typename T> void add_readonly_single_input(const T *value, StringRef expected_name = "") { - this->add_readonly_single_input(GVSpan::FromSingle(CPPType::get<T>(), value, min_array_size_)); + this->add_readonly_single_input(GVSpan::FromSingle(CPPType::get<T>(), value, min_array_size_), + expected_name); } - void add_readonly_single_input(GVSpan ref) + void add_readonly_single_input(GVSpan ref, StringRef expected_name = "") { - this->assert_current_param_type(MFParamType::ForSingleInput(ref.type())); + this->assert_current_param_type(MFParamType::ForSingleInput(ref.type()), expected_name); BLI_assert(ref.size() >= min_array_size_); virtual_spans_.append(ref); } - void add_readonly_vector_input(GVArraySpan ref) + void add_readonly_vector_input(GVArraySpan ref, StringRef expected_name = "") { - this->assert_current_param_type(MFParamType::ForVectorInput(ref.type())); + this->assert_current_param_type(MFParamType::ForVectorInput(ref.type()), expected_name); BLI_assert(ref.size() >= min_array_size_); virtual_array_spans_.append(ref); } - template<typename T> void add_uninitialized_single_output(T *value) + template<typename T> void add_uninitialized_single_output(T *value, StringRef expected_name = "") { - this->add_uninitialized_single_output(GMutableSpan(CPPType::get<T>(), value, 1)); + this->add_uninitialized_single_output(GMutableSpan(CPPType::get<T>(), value, 1), + expected_name); } - void add_uninitialized_single_output(GMutableSpan ref) + void add_uninitialized_single_output(GMutableSpan ref, StringRef expected_name = "") { - this->assert_current_param_type(MFParamType::ForSingleOutput(ref.type())); + this->assert_current_param_type(MFParamType::ForSingleOutput(ref.type()), expected_name); BLI_assert(ref.size() >= min_array_size_); mutable_spans_.append(ref); } - void add_vector_output(GVectorArray &vector_array) + void add_vector_output(GVectorArray &vector_array, StringRef expected_name = "") { - this->assert_current_param_type(MFParamType::ForVectorOutput(vector_array.type())); + this->assert_current_param_type(MFParamType::ForVectorOutput(vector_array.type()), + expected_name); BLI_assert(vector_array.size() >= min_array_size_); vector_arrays_.append(&vector_array); } - void add_single_mutable(GMutableSpan ref) + void add_single_mutable(GMutableSpan ref, StringRef expected_name = "") { - this->assert_current_param_type(MFParamType::ForMutableSingle(ref.type())); + this->assert_current_param_type(MFParamType::ForMutableSingle(ref.type()), expected_name); BLI_assert(ref.size() >= min_array_size_); mutable_spans_.append(ref); } - void add_vector_mutable(GVectorArray &vector_array) + void add_vector_mutable(GVectorArray &vector_array, StringRef expected_name = "") { - this->assert_current_param_type(MFParamType::ForMutableVector(vector_array.type())); + this->assert_current_param_type(MFParamType::ForMutableVector(vector_array.type()), + expected_name); BLI_assert(vector_array.size() >= min_array_size_); vector_arrays_.append(&vector_array); } @@ -119,11 +123,17 @@ class MFParamsBuilder { } private: - void assert_current_param_type(MFParamType param_type) + void assert_current_param_type(MFParamType param_type, StringRef expected_name = "") { - UNUSED_VARS_NDEBUG(param_type); + UNUSED_VARS_NDEBUG(param_type, expected_name); #ifdef DEBUG int param_index = this->current_param_index(); + + if (expected_name != "") { + StringRef actual_name = signature_->param_names[param_index]; + BLI_assert(actual_name == expected_name); + } + MFParamType expected_type = signature_->param_types[param_index]; BLI_assert(expected_type == param_type); #endif diff --git a/source/blender/functions/FN_multi_function_signature.hh b/source/blender/functions/FN_multi_function_signature.hh index ba79dddff16..3c09d1c961c 100644 --- a/source/blender/functions/FN_multi_function_signature.hh +++ b/source/blender/functions/FN_multi_function_signature.hh @@ -32,10 +32,9 @@ namespace blender::fn { struct MFSignature { std::string function_name; - /* Use RawAllocator so that a MultiFunction can have static storage duration. */ - RawVector<std::string> param_names; - RawVector<MFParamType> param_types; - RawVector<int> param_data_indices; + Vector<std::string> param_names; + Vector<MFParamType> param_types; + Vector<int> param_data_indices; bool depends_on_context = false; int data_index(int param_index) const diff --git a/source/blender/functions/FN_spans.hh b/source/blender/functions/FN_spans.hh index d8b381199cc..62fa7c8ed4b 100644 --- a/source/blender/functions/FN_spans.hh +++ b/source/blender/functions/FN_spans.hh @@ -51,12 +51,12 @@ namespace blender::fn { class GSpan { private: const CPPType *type_; - const void *buffer_; + const void *data_; int64_t size_; public: GSpan(const CPPType &type, const void *buffer, int64_t size) - : type_(&type), buffer_(buffer), size_(size) + : type_(&type), data_(buffer), size_(size) { BLI_assert(size >= 0); BLI_assert(buffer != nullptr || size == 0); @@ -87,21 +87,21 @@ class GSpan { return size_; } - const void *buffer() const + const void *data() const { - return buffer_; + return data_; } const void *operator[](int64_t index) const { BLI_assert(index < size_); - return POINTER_OFFSET(buffer_, type_->size() * index); + return POINTER_OFFSET(data_, type_->size() * index); } template<typename T> Span<T> typed() const { BLI_assert(type_->is<T>()); - return Span<T>((const T *)buffer_, size_); + return Span<T>((const T *)data_, size_); } }; @@ -112,12 +112,12 @@ class GSpan { class GMutableSpan { private: const CPPType *type_; - void *buffer_; + void *data_; int64_t size_; public: GMutableSpan(const CPPType &type, void *buffer, int64_t size) - : type_(&type), buffer_(buffer), size_(size) + : type_(&type), data_(buffer), size_(size) { BLI_assert(size >= 0); BLI_assert(buffer != nullptr || size == 0); @@ -136,7 +136,7 @@ class GMutableSpan { operator GSpan() const { - return GSpan(*type_, buffer_, size_); + return GSpan(*type_, data_, size_); } const CPPType &type() const @@ -154,21 +154,21 @@ class GMutableSpan { return size_; } - void *buffer() + void *data() { - return buffer_; + return data_; } void *operator[](int64_t index) { BLI_assert(index < size_); - return POINTER_OFFSET(buffer_, type_->size() * index); + return POINTER_OFFSET(data_, type_->size() * index); } template<typename T> MutableSpan<T> typed() { BLI_assert(type_->is<T>()); - return MutableSpan<T>((T *)buffer_, size_); + return MutableSpan<T>((T *)data_, size_); } }; @@ -209,6 +209,20 @@ template<typename T> struct VSpanBase { return false; } + bool is_full_array() const + { + switch (category_) { + case VSpanCategory::Single: + return virtual_size_ == 1; + case VSpanCategory::FullArray: + return true; + case VSpanCategory::FullPointerArray: + return virtual_size_ <= 1; + } + BLI_assert(false); + return false; + } + bool is_empty() const { return this->virtual_size_ == 0; @@ -285,6 +299,22 @@ template<typename T> class VSpan : public VSpanBase<T> { BLI_assert(false); return *this->data_.single.data; } + + const T &as_single_element() const + { + BLI_assert(this->is_single_element()); + return (*this)[0]; + } + + Span<T> as_full_array() const + { + BLI_assert(this->is_full_array()); + if (this->virtual_size_ == 0) { + return Span<T>(); + } + const T *data = &(*this)[0]; + return Span<T>(data, this->virtual_size_); + } }; /** @@ -311,7 +341,7 @@ class GVSpan : public VSpanBase<void> { this->type_ = &values.type(); this->virtual_size_ = values.size(); this->category_ = VSpanCategory::FullArray; - this->data_.full_array.data = values.buffer(); + this->data_.full_array.data = values.data(); } GVSpan(GMutableSpan values) : GVSpan(GSpan(values)) @@ -395,6 +425,16 @@ class GVSpan : public VSpanBase<void> { return (*this)[0]; } + GSpan as_full_array() const + { + BLI_assert(this->is_full_array()); + if (this->virtual_size_ == 0) { + return GSpan(*this->type_); + } + const void *data = (*this)[0]; + return GSpan(*this->type_, data, this->virtual_size_); + } + void materialize_to_uninitialized(void *dst) const { this->materialize_to_uninitialized(IndexRange(virtual_size_), dst); diff --git a/source/blender/functions/intern/attributes_ref.cc b/source/blender/functions/intern/attributes_ref.cc index 7bfcc69671a..4686e217911 100644 --- a/source/blender/functions/intern/attributes_ref.cc +++ b/source/blender/functions/intern/attributes_ref.cc @@ -25,7 +25,7 @@ AttributesInfoBuilder::~AttributesInfoBuilder() } } -void AttributesInfoBuilder::add(StringRef name, const CPPType &type, const void *default_value) +bool AttributesInfoBuilder::add(StringRef name, const CPPType &type, const void *default_value) { if (names_.add_as(name)) { types_.append(&type); @@ -36,10 +36,15 @@ void AttributesInfoBuilder::add(StringRef name, const CPPType &type, const void void *dst = allocator_.allocate(type.size(), type.alignment()); type.copy_to_uninitialized(default_value, dst); defaults_.append(dst); + return true; } else { - /* The same name can be added more than once as long as the type is always the same. */ - BLI_assert(types_[names_.index_of_as(name)] == &type); + const CPPType &stored_type = *types_[names_.index_of_as(name)]; + if (stored_type != type) { + std::cout << "Warning: Tried to add an attribute twice with different types (" << name + << ": " << stored_type.name() << ", " << type.name() << ").\n"; + } + return false; } } diff --git a/source/blender/functions/intern/multi_function_builder.cc b/source/blender/functions/intern/multi_function_builder.cc index 06084247e66..c9e8b88ba03 100644 --- a/source/blender/functions/intern/multi_function_builder.cc +++ b/source/blender/functions/intern/multi_function_builder.cc @@ -34,7 +34,7 @@ void CustomMF_GenericConstant::call(IndexMask mask, MFContext UNUSED(context)) const { GMutableSpan output = params.uninitialized_single_output(0); - type_.fill_uninitialized_indices(value_, output.buffer(), mask); + type_.fill_uninitialized_indices(value_, output.data(), mask); } uint64_t CustomMF_GenericConstant::hash() const @@ -111,7 +111,7 @@ void CustomMF_DefaultOutput::call(IndexMask mask, MFParams params, MFContext UNU if (param_type.data_type().is_single()) { GMutableSpan span = params.uninitialized_single_output(param_index); const CPPType &type = span.type(); - type.fill_uninitialized_indices(type.default_value(), span.buffer(), mask); + type.fill_uninitialized_indices(type.default_value(), span.data(), mask); } } } diff --git a/source/blender/functions/intern/multi_function_network_evaluation.cc b/source/blender/functions/intern/multi_function_network_evaluation.cc index 58577e31c42..25e983d2eeb 100644 --- a/source/blender/functions/intern/multi_function_network_evaluation.cc +++ b/source/blender/functions/intern/multi_function_network_evaluation.cc @@ -390,7 +390,7 @@ BLI_NOINLINE void MFNetworkEvaluator::initialize_remaining_outputs( case MFDataType::Single: { GVSpan values = storage.get_single_input__full(*socket); GMutableSpan output_values = params.uninitialized_single_output(param_index); - values.materialize_to_uninitialized(storage.mask(), output_values.buffer()); + values.materialize_to_uninitialized(storage.mask(), output_values.data()); break; } case MFDataType::Vector: { @@ -524,11 +524,11 @@ MFNetworkEvaluationStorage::~MFNetworkEvaluationStorage() GMutableSpan span = value->span; const CPPType &type = span.type(); if (value->is_single_allocated) { - type.destruct(span.buffer()); + type.destruct(span.data()); } else { - type.destruct_indices(span.buffer(), mask_); - MEM_freeN(span.buffer()); + type.destruct_indices(span.data(), mask_); + MEM_freeN(span.data()); } } else if (any_value->type == ValueType::OwnVector) { @@ -634,11 +634,11 @@ void MFNetworkEvaluationStorage::finish_input_socket(const MFInputSocket &socket GMutableSpan span = value->span; const CPPType &type = span.type(); if (value->is_single_allocated) { - type.destruct(span.buffer()); + type.destruct(span.data()); } else { - type.destruct_indices(span.buffer(), mask_); - MEM_freeN(span.buffer()); + type.destruct_indices(span.data(), mask_); + MEM_freeN(span.data()); } value_per_output_id_[origin.id()] = nullptr; } @@ -791,7 +791,7 @@ GMutableSpan MFNetworkEvaluationStorage::get_mutable_single__full(const MFInputS BLI_assert(to_any_value->type == ValueType::OutputSingle); GMutableSpan span = ((OutputSingleValue *)to_any_value)->span; GVSpan virtual_span = this->get_single_input__full(input); - virtual_span.materialize_to_uninitialized(mask_, span.buffer()); + virtual_span.materialize_to_uninitialized(mask_, span.data()); return span; } @@ -808,7 +808,7 @@ GMutableSpan MFNetworkEvaluationStorage::get_mutable_single__full(const MFInputS GVSpan virtual_span = this->get_single_input__full(input); void *new_buffer = MEM_mallocN_aligned(min_array_size_ * type.size(), type.alignment(), AT); GMutableSpan new_array_ref(type, new_buffer, min_array_size_); - virtual_span.materialize_to_uninitialized(mask_, new_array_ref.buffer()); + virtual_span.materialize_to_uninitialized(mask_, new_array_ref.data()); OwnSingleValue *new_value = allocator_.construct<OwnSingleValue>( new_array_ref, to.targets().size(), false); @@ -953,7 +953,7 @@ GVSpan MFNetworkEvaluationStorage::get_single_input__full(const MFInputSocket &s if (any_value->type == ValueType::OwnSingle) { OwnSingleValue *value = (OwnSingleValue *)any_value; if (value->is_single_allocated) { - return GVSpan::FromSingle(value->span.type(), value->span.buffer(), min_array_size_); + return GVSpan::FromSingle(value->span.type(), value->span.data(), min_array_size_); } else { return value->span; diff --git a/source/blender/functions/intern/multi_function_network_optimization.cc b/source/blender/functions/intern/multi_function_network_optimization.cc index f1e047f01a1..af1e77aa355 100644 --- a/source/blender/functions/intern/multi_function_network_optimization.cc +++ b/source/blender/functions/intern/multi_function_network_optimization.cc @@ -28,6 +28,7 @@ #include "BLI_disjoint_set.hh" #include "BLI_ghash.h" #include "BLI_map.hh" +#include "BLI_multi_value_map.hh" #include "BLI_rand.h" #include "BLI_stack.hh" @@ -265,7 +266,7 @@ static Array<MFOutputSocket *> add_constant_folded_sockets(const MultiFunction & case MFDataType::Single: { const CPPType &cpp_type = data_type.single_type(); GMutableSpan array = params.computed_array(param_index); - void *buffer = array.buffer(); + void *buffer = array.data(); resources.add(buffer, array.type().destruct_cb(), AT); constant_fn = &resources.construct<CustomMF_GenericConstant>(AT, cpp_type, buffer); @@ -403,15 +404,15 @@ static Array<uint64_t> compute_node_hashes(MFNetwork &network) return node_hashes; } -static Map<uint64_t, Vector<MFNode *, 1>> group_nodes_by_hash(MFNetwork &network, - Span<uint64_t> node_hashes) +static MultiValueMap<uint64_t, MFNode *> group_nodes_by_hash(MFNetwork &network, + Span<uint64_t> node_hashes) { - Map<uint64_t, Vector<MFNode *, 1>> nodes_by_hash; + MultiValueMap<uint64_t, MFNode *> nodes_by_hash; for (int id : IndexRange(network.node_id_amount())) { MFNode *node = network.node_or_null_by_id(id); if (node != nullptr) { uint64_t node_hash = node_hashes[id]; - nodes_by_hash.lookup_or_add_default(node_hash).append(node); + nodes_by_hash.add(node_hash, node); } } return nodes_by_hash; @@ -456,7 +457,7 @@ static bool nodes_output_same_values(DisjointSet &cache, const MFNode &a, const } static void relink_duplicate_nodes(MFNetwork &network, - Map<uint64_t, Vector<MFNode *, 1>> &nodes_by_hash) + MultiValueMap<uint64_t, MFNode *> &nodes_by_hash) { DisjointSet same_node_cache{network.node_id_amount()}; @@ -494,7 +495,7 @@ static void relink_duplicate_nodes(MFNetwork &network, void common_subnetwork_elimination(MFNetwork &network) { Array<uint64_t> node_hashes = compute_node_hashes(network); - Map<uint64_t, Vector<MFNode *, 1>> nodes_by_hash = group_nodes_by_hash(network, node_hashes); + MultiValueMap<uint64_t, MFNode *> nodes_by_hash = group_nodes_by_hash(network, node_hashes); relink_duplicate_nodes(network, nodes_by_hash); } diff --git a/tests/gtests/functions/FN_array_spans_test.cc b/source/blender/functions/tests/FN_array_spans_test.cc index 6b7bb8429ff..9a632b58be8 100644 --- a/tests/gtests/functions/FN_array_spans_test.cc +++ b/source/blender/functions/tests/FN_array_spans_test.cc @@ -7,7 +7,7 @@ #include "BLI_array.hh" -namespace blender::fn { +namespace blender::fn::tests { TEST(virtual_array_span, EmptyConstructor) { @@ -129,4 +129,4 @@ TEST(generic_virtual_array_span, IsSingleArray2) EXPECT_FALSE(converted.is_single_array()); } -} // namespace blender::fn +} // namespace blender::fn::tests diff --git a/tests/gtests/functions/FN_attributes_ref_test.cc b/source/blender/functions/tests/FN_attributes_ref_test.cc index 9c7cce83493..3a5e4743c1e 100644 --- a/tests/gtests/functions/FN_attributes_ref_test.cc +++ b/source/blender/functions/tests/FN_attributes_ref_test.cc @@ -5,7 +5,7 @@ #include "testing/testing.h" -namespace blender::fn { +namespace blender::fn::tests { TEST(attributes_info, BuildEmpty) { @@ -68,10 +68,10 @@ TEST(mutable_attributes_ref, ComplexTest) MutableAttributesRef attributes{info, buffers, IndexRange(1, 3)}; EXPECT_EQ(attributes.size(), 3); EXPECT_EQ(attributes.info().size(), 4); - EXPECT_EQ(attributes.get("Position").buffer(), positions.data() + 1); - EXPECT_EQ(attributes.get("ID").buffer(), ids.data() + 1); - EXPECT_EQ(attributes.get("Size").buffer(), sizes.data() + 1); - EXPECT_EQ(attributes.get("Name").buffer(), names.data() + 1); + EXPECT_EQ(attributes.get("Position").data(), positions.data() + 1); + EXPECT_EQ(attributes.get("ID").data(), ids.data() + 1); + EXPECT_EQ(attributes.get("Size").data(), sizes.data() + 1); + EXPECT_EQ(attributes.get("Name").data(), names.data() + 1); EXPECT_EQ(attributes.get("ID").size(), 3); EXPECT_EQ(attributes.get<uint>("ID").size(), 3); @@ -94,4 +94,4 @@ TEST(mutable_attributes_ref, ComplexTest) EXPECT_EQ(ids[2], 100); } -} // namespace blender::fn +} // namespace blender::fn::tests diff --git a/tests/gtests/functions/FN_cpp_type_test.cc b/source/blender/functions/tests/FN_cpp_type_test.cc index 85fc1105c25..29368b251cc 100644 --- a/tests/gtests/functions/FN_cpp_type_test.cc +++ b/source/blender/functions/tests/FN_cpp_type_test.cc @@ -4,7 +4,7 @@ #include "FN_cpp_type.hh" -namespace blender::fn { +namespace blender::fn::tests { static const int default_constructed_value = 1; static const int copy_constructed_value = 2; @@ -62,7 +62,7 @@ struct TestType { return stream; } - friend bool operator==(const TestType &a, const TestType &b) + friend bool operator==(const TestType &UNUSED(a), const TestType &UNUSED(b)) { return false; } @@ -73,7 +73,11 @@ struct TestType { } }; -MAKE_CPP_TYPE(TestType, TestType) +} // namespace blender::fn::tests + +MAKE_CPP_TYPE(TestType, blender::fn::tests::TestType) + +namespace blender::fn::tests { const CPPType &CPPType_TestType = CPPType::get<TestType>(); @@ -318,4 +322,4 @@ TEST(cpp_type, DebugPrint) EXPECT_EQ(text, "42"); } -} // namespace blender::fn +} // namespace blender::fn::tests diff --git a/tests/gtests/functions/FN_generic_vector_array_test.cc b/source/blender/functions/tests/FN_generic_vector_array_test.cc index cfca678ad27..77ec05f12dc 100644 --- a/tests/gtests/functions/FN_generic_vector_array_test.cc +++ b/source/blender/functions/tests/FN_generic_vector_array_test.cc @@ -4,7 +4,7 @@ #include "testing/testing.h" -namespace blender::fn { +namespace blender::fn::tests { TEST(generic_vector_array, Constructor) { @@ -98,4 +98,4 @@ TEST(generic_vector_array, Extend) EXPECT_EQ(ref[0][0], 3); } -} // namespace blender::fn +} // namespace blender::fn::tests diff --git a/tests/gtests/functions/FN_multi_function_network_test.cc b/source/blender/functions/tests/FN_multi_function_network_test.cc index 9f16b71bb10..53290e9170c 100644 --- a/tests/gtests/functions/FN_multi_function_network_test.cc +++ b/source/blender/functions/tests/FN_multi_function_network_test.cc @@ -6,7 +6,7 @@ #include "FN_multi_function_network.hh" #include "FN_multi_function_network_evaluation.hh" -namespace blender::fn { +namespace blender::fn::tests { TEST(multi_function_network, Test1) { @@ -250,4 +250,4 @@ TEST(multi_function_network, Test2) } } -} // namespace blender::fn +} // namespace blender::fn::tests diff --git a/tests/gtests/functions/FN_multi_function_test.cc b/source/blender/functions/tests/FN_multi_function_test.cc index 8b5fb060c36..6acb6e22b01 100644 --- a/tests/gtests/functions/FN_multi_function_test.cc +++ b/source/blender/functions/tests/FN_multi_function_test.cc @@ -5,7 +5,7 @@ #include "FN_multi_function.hh" #include "FN_multi_function_builder.hh" -namespace blender::fn { +namespace blender::fn::tests { class AddFunction : public MultiFunction { public: @@ -382,4 +382,4 @@ TEST(multi_function, CustomMF_Convert) EXPECT_EQ(outputs[2], 9); } -} // namespace blender::fn +} // namespace blender::fn::tests diff --git a/tests/gtests/functions/FN_spans_test.cc b/source/blender/functions/tests/FN_spans_test.cc index 3172cdc7170..fbcf1fda71e 100644 --- a/tests/gtests/functions/FN_spans_test.cc +++ b/source/blender/functions/tests/FN_spans_test.cc @@ -4,7 +4,7 @@ #include "FN_spans.hh" -namespace blender::fn { +namespace blender::fn::tests { TEST(generic_span, TypeConstructor) { @@ -56,6 +56,7 @@ TEST(virtual_span, EmptyConstructor) EXPECT_EQ(span.size(), 0); EXPECT_TRUE(span.is_empty()); EXPECT_FALSE(span.is_single_element()); + EXPECT_TRUE(span.is_full_array()); GVSpan converted(span); EXPECT_EQ(converted.type(), CPPType::get<int>()); @@ -73,6 +74,7 @@ TEST(virtual_span, SpanConstructor) EXPECT_EQ(virtual_span[2], 8); EXPECT_EQ(virtual_span[3], 6); EXPECT_FALSE(virtual_span.is_single_element()); + EXPECT_TRUE(virtual_span.is_full_array()); GVSpan converted(span); EXPECT_EQ(converted.type(), CPPType::get<int>()); @@ -93,6 +95,7 @@ TEST(virtual_span, PointerSpanConstructor) EXPECT_EQ(span[2], 6); EXPECT_EQ(&span[1], &x2); EXPECT_FALSE(span.is_single_element()); + EXPECT_FALSE(span.is_full_array()); GVSpan converted(span); EXPECT_EQ(converted.type(), CPPType::get<int>()); @@ -115,6 +118,7 @@ TEST(virtual_span, SingleConstructor) EXPECT_EQ(&span[1], &value); EXPECT_EQ(&span[2], &value); EXPECT_TRUE(span.is_single_element()); + EXPECT_FALSE(span.is_full_array()); GVSpan converted(span); EXPECT_EQ(converted.type(), CPPType::get<int>()); @@ -130,6 +134,7 @@ TEST(generic_virtual_span, TypeConstructor) EXPECT_EQ(span.size(), 0); EXPECT_TRUE(span.is_empty()); EXPECT_FALSE(span.is_single_element()); + EXPECT_TRUE(span.is_full_array()); VSpan<int> converted = span.typed<int>(); EXPECT_EQ(converted.size(), 0); @@ -146,6 +151,7 @@ TEST(generic_virtual_span, GenericSpanConstructor) EXPECT_EQ(span[2], &values[2]); EXPECT_EQ(span[3], &values[3]); EXPECT_FALSE(span.is_single_element()); + EXPECT_TRUE(span.is_full_array()); int materialized[4] = {0}; span.materialize_to_uninitialized(materialized); @@ -172,6 +178,7 @@ TEST(generic_virtual_span, SpanConstructor) EXPECT_EQ(span[1], &values[1]); EXPECT_EQ(span[2], &values[2]); EXPECT_FALSE(span.is_single_element()); + EXPECT_TRUE(span.is_full_array()); int materialized[3] = {0}; span.materialize_to_uninitialized(materialized); @@ -197,6 +204,7 @@ TEST(generic_virtual_span, SingleConstructor) EXPECT_EQ(span[2], &value); EXPECT_TRUE(span.is_single_element()); EXPECT_EQ(span.as_single_element(), &value); + EXPECT_FALSE(span.is_full_array()); int materialized[3] = {0}; span.materialize_to_uninitialized({1, 2}, materialized); @@ -211,4 +219,4 @@ TEST(generic_virtual_span, SingleConstructor) EXPECT_EQ(converted[2], 5); } -} // namespace blender::fn +} // namespace blender::fn::tests diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpenciloffset.c b/source/blender/gpencil_modifiers/intern/MOD_gpenciloffset.c index 9cc3712e8f4..75f929e979e 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpenciloffset.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpenciloffset.c @@ -105,19 +105,23 @@ static void deformStroke(GpencilModifierData *md, bGPDspoint *pt = &gps->points[i]; MDeformVert *dvert = gps->dvert != NULL ? &gps->dvert[i] : NULL; - /* verify vertex group */ + /* Verify vertex group. */ const float weight = get_modifier_point_weight( dvert, (mmd->flag & GP_OFFSET_INVERT_VGROUP) != 0, def_nr); if (weight < 0.0f) { continue; } - /* calculate matrix */ + /* Calculate matrix. */ mul_v3_v3fl(loc, mmd->loc, weight); mul_v3_v3fl(rot, mmd->rot, weight); mul_v3_v3fl(scale, mmd->scale, weight); add_v3_fl(scale, 1.0); loc_eul_size_to_mat4(mat, loc, rot, scale); + /* Apply scale to thickness. */ + float unit_scale = (scale[0] + scale[1] + scale[2]) / 3.0f; + pt->pressure *= unit_scale; + mul_m4_v3(mat, &pt->x); } /* Calc geometry data. */ diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index 4f90482d16e..3c7ff51ea24 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -52,27 +52,25 @@ set(INC_SYS ) set(SRC - intern/gpu_attr_binding.c - intern/gpu_batch.c + intern/gpu_attr_binding.cc + intern/gpu_batch.cc intern/gpu_batch_presets.c intern/gpu_batch_utils.c intern/gpu_buffers.c intern/gpu_codegen.c - intern/gpu_context.cpp - intern/gpu_debug.c - intern/gpu_draw.c - intern/gpu_draw_smoke.c - intern/gpu_element.c - intern/gpu_extensions.c - intern/gpu_framebuffer.c - intern/gpu_immediate.c + intern/gpu_context.cc + intern/gpu_debug.cc + intern/gpu_element.cc + intern/gpu_extensions.cc + intern/gpu_framebuffer.cc + intern/gpu_immediate.cc intern/gpu_immediate_util.c intern/gpu_init_exit.c intern/gpu_material.c intern/gpu_material_library.c intern/gpu_matrix.c intern/gpu_node_graph.c - intern/gpu_platform.c + intern/gpu_platform.cc intern/gpu_primitive.c intern/gpu_select.c intern/gpu_select_pick.c @@ -81,6 +79,8 @@ set(SRC intern/gpu_shader_interface.c intern/gpu_state.c intern/gpu_texture.c + intern/gpu_texture_image.cc + intern/gpu_texture_smoke.cc intern/gpu_uniformbuffer.c intern/gpu_vertex_buffer.c intern/gpu_vertex_format.c diff --git a/source/blender/gpu/GPU_debug.h b/source/blender/gpu/GPU_debug.h index 8928581ee08..fc6ca791664 100644 --- a/source/blender/gpu/GPU_debug.h +++ b/source/blender/gpu/GPU_debug.h @@ -24,8 +24,6 @@ #ifndef __GPU_DEBUG_H__ #define __GPU_DEBUG_H__ -#include "GPU_glew.h" - #ifdef __cplusplus extern "C" { #endif diff --git a/source/blender/gpu/GPU_draw.h b/source/blender/gpu/GPU_draw.h index b364bd0ef95..01af654b52f 100644 --- a/source/blender/gpu/GPU_draw.h +++ b/source/blender/gpu/GPU_draw.h @@ -58,21 +58,7 @@ float GPU_get_anisotropic(void); void GPU_paint_update_image( struct Image *ima, struct ImageUser *iuser, int x, int y, int w, int h); -void GPU_create_gl_tex(unsigned int *bind, - unsigned int *rect, - float *frect, - int rectw, - int recth, - int textarget, - bool mipmap, - bool half_float, - bool use_srgb, - struct Image *ima); -void GPU_create_gl_tex_compressed(unsigned int *bind, - int textarget, - struct Image *ima, - struct ImBuf *ibuf); -bool GPU_upload_dxt_texture(struct ImBuf *ibuf, bool use_srgb); +bool GPU_upload_dxt_texture(struct ImBuf *ibuf, bool use_srgb, uint *bindcode); void GPU_free_image(struct Image *ima); void GPU_free_images(struct Main *bmain); void GPU_free_images_anim(struct Main *bmain); diff --git a/source/blender/gpu/GPU_framebuffer.h b/source/blender/gpu/GPU_framebuffer.h index 1a9fb1bad7a..ef6be0b2778 100644 --- a/source/blender/gpu/GPU_framebuffer.h +++ b/source/blender/gpu/GPU_framebuffer.h @@ -32,7 +32,7 @@ extern "C" { typedef struct GPUAttachment { struct GPUTexture *tex; - int mip, layer; + int layer, mip; } GPUAttachment; typedef enum eGPUFrameBufferBits { @@ -120,35 +120,35 @@ void GPU_framebuffer_config_array(GPUFrameBuffer *fb, const GPUAttachment *confi #define GPU_ATTACHMENT_NONE \ { \ - .tex = NULL, .layer = -1, .mip = 0, \ + NULL, -1, 0, \ } #define GPU_ATTACHMENT_LEAVE \ { \ - .tex = NULL, .layer = -1, .mip = -1, \ + NULL, -1, -1, \ } #define GPU_ATTACHMENT_TEXTURE(_tex) \ { \ - .tex = _tex, .layer = -1, .mip = 0, \ + _tex, -1, 0, \ } #define GPU_ATTACHMENT_TEXTURE_MIP(_tex, _mip) \ { \ - .tex = _tex, .layer = -1, .mip = _mip, \ + _tex, -1, _mip, \ } #define GPU_ATTACHMENT_TEXTURE_LAYER(_tex, _layer) \ { \ - .tex = _tex, .layer = _layer, .mip = 0, \ + _tex, _layer, 0, \ } #define GPU_ATTACHMENT_TEXTURE_LAYER_MIP(_tex, _layer, _mip) \ { \ - .tex = _tex, .layer = _layer, .mip = _mip, \ + _tex, _layer, _mip, \ } #define GPU_ATTACHMENT_TEXTURE_CUBEFACE(_tex, _face) \ { \ - .tex = _tex, .layer = _face, .mip = 0, \ + _tex, _face, 0, \ } #define GPU_ATTACHMENT_TEXTURE_CUBEFACE_MIP(_tex, _face, _mip) \ { \ - .tex = _tex, .layer = _face, .mip = _mip, \ + _tex, _face, _mip, \ } /* Framebuffer operations */ @@ -212,7 +212,7 @@ GPUOffScreen *GPU_offscreen_create( void GPU_offscreen_free(GPUOffScreen *ofs); void GPU_offscreen_bind(GPUOffScreen *ofs, bool save); void GPU_offscreen_unbind(GPUOffScreen *ofs, bool restore); -void GPU_offscreen_read_pixels(GPUOffScreen *ofs, int type, void *pixels); +void GPU_offscreen_read_pixels(GPUOffScreen *ofs, eGPUDataFormat type, void *pixels); void GPU_offscreen_draw_to_screen(GPUOffScreen *ofs, int x, int y); int GPU_offscreen_width(const GPUOffScreen *ofs); int GPU_offscreen_height(const GPUOffScreen *ofs); diff --git a/source/blender/gpu/GPU_platform.h b/source/blender/gpu/GPU_platform.h index f199a748cb5..104d5ef0ddc 100644 --- a/source/blender/gpu/GPU_platform.h +++ b/source/blender/gpu/GPU_platform.h @@ -25,10 +25,7 @@ #define __GPU_PLATFORM_H__ #include "BLI_sys_types.h" - -#ifdef __cplusplus -extern "C" { -#endif +#include "BLI_utildefines.h" /* GPU platform support */ @@ -43,6 +40,8 @@ typedef enum eGPUDeviceType { GPU_DEVICE_ANY = (0xff), } eGPUDeviceType; +ENUM_OPERATORS(eGPUDeviceType) + typedef enum eGPUOSType { GPU_OS_WIN = (1 << 8), GPU_OS_MAC = (1 << 9), @@ -63,6 +62,10 @@ typedef enum eGPUSupportLevel { GPU_SUPPORT_LEVEL_UNSUPPORTED, } eGPUSupportLevel; +#ifdef __cplusplus +extern "C" { +#endif + bool GPU_type_matches(eGPUDeviceType device, eGPUOSType os, eGPUDriverType driver); eGPUSupportLevel GPU_platform_support_level(void); const char *GPU_platform_support_level_key(void); diff --git a/source/blender/gpu/GPU_texture.h b/source/blender/gpu/GPU_texture.h index 813ee71eb22..d7f5d6272f4 100644 --- a/source/blender/gpu/GPU_texture.h +++ b/source/blender/gpu/GPU_texture.h @@ -41,6 +41,15 @@ struct PreviewImage; struct GPUFrameBuffer; typedef struct GPUTexture GPUTexture; +/* Used to get the correct gpu texture from an Image datablock. */ +typedef enum eGPUTextureTarget { + TEXTARGET_2D = 0, + TEXTARGET_CUBE_MAP, + TEXTARGET_2D_ARRAY, + TEXTARGET_TILE_MAPPING, + TEXTARGET_COUNT, +} eGPUTextureTarget; + /* GPU Samplers state * - Specify the sampler state to bind a texture with. * - Internally used by textures. @@ -222,16 +231,16 @@ GPUTexture *GPU_texture_create_cube_array( GPUTexture *GPU_texture_create_from_vertbuf(struct GPUVertBuf *vert); GPUTexture *GPU_texture_create_buffer(eGPUTextureFormat data_type, const uint buffer); -GPUTexture *GPU_texture_from_bindcode(int textarget, int bindcode); +GPUTexture *GPU_texture_from_bindcode(eGPUTextureTarget target, int bindcode); GPUTexture *GPU_texture_from_blender(struct Image *ima, struct ImageUser *iuser, struct ImBuf *ibuf, - int textarget); + eGPUTextureTarget target); /* movie clip drawing */ GPUTexture *GPU_texture_from_movieclip(struct MovieClip *clip, struct MovieClipUser *cuser, - int textarget); + eGPUTextureTarget target); void GPU_free_texture_movieclip(struct MovieClip *clip); void GPU_texture_add_mipmap(GPUTexture *tex, diff --git a/source/blender/gpu/intern/gpu_attr_binding.c b/source/blender/gpu/intern/gpu_attr_binding.cc index 6cb60884620..6cb60884620 100644 --- a/source/blender/gpu/intern/gpu_attr_binding.c +++ b/source/blender/gpu/intern/gpu_attr_binding.cc diff --git a/source/blender/gpu/intern/gpu_attr_binding_private.h b/source/blender/gpu/intern/gpu_attr_binding_private.h index 301ec3333dd..7df403a3ea5 100644 --- a/source/blender/gpu/intern/gpu_attr_binding_private.h +++ b/source/blender/gpu/intern/gpu_attr_binding_private.h @@ -29,6 +29,11 @@ #include "GPU_shader_interface.h" #include "GPU_vertex_format.h" +#ifdef __cplusplus +extern "C" { +#endif + +/* TODO(fclem) remove, use shaderface directly. */ void AttrBinding_clear(GPUAttrBinding *binding); void get_attr_locations(const GPUVertFormat *format, @@ -36,4 +41,8 @@ void get_attr_locations(const GPUVertFormat *format, const GPUShaderInterface *shaderface); uint read_attr_location(const GPUAttrBinding *binding, uint a_idx); +#ifdef __cplusplus +} +#endif + #endif /* __GPU_ATTR_BINDING_PRIVATE_H__ */ diff --git a/source/blender/gpu/intern/gpu_batch.c b/source/blender/gpu/intern/gpu_batch.cc index 5f77f13c135..43a8d000547 100644 --- a/source/blender/gpu/intern/gpu_batch.c +++ b/source/blender/gpu/intern/gpu_batch.cc @@ -89,7 +89,7 @@ GPUBatch *GPU_batch_create_ex(GPUPrimType prim_type, GPUIndexBuf *elem, uint owns_flag) { - GPUBatch *batch = MEM_callocN(sizeof(GPUBatch), "GPUBatch"); + GPUBatch *batch = (GPUBatch *)MEM_callocN(sizeof(GPUBatch), "GPUBatch"); GPU_batch_init_ex(batch, prim_type, verts, elem, owns_flag); return batch; } @@ -327,10 +327,10 @@ static GLuint batch_vao_get(GPUBatch *batch) } /* Init dynamic arrays and let the branch below set the values. */ batch->dynamic_vaos.count = GPU_BATCH_VAO_DYN_ALLOC_COUNT; - batch->dynamic_vaos.interfaces = MEM_callocN( + batch->dynamic_vaos.interfaces = (const GPUShaderInterface **)MEM_callocN( batch->dynamic_vaos.count * sizeof(GPUShaderInterface *), "dyn vaos interfaces"); - batch->dynamic_vaos.vao_ids = MEM_callocN(batch->dynamic_vaos.count * sizeof(GLuint), - "dyn vaos ids"); + batch->dynamic_vaos.vao_ids = (GLuint *)MEM_callocN( + batch->dynamic_vaos.count * sizeof(GLuint), "dyn vaos ids"); } } @@ -346,11 +346,11 @@ static GLuint batch_vao_get(GPUBatch *batch) /* Not enough place, realloc the array. */ i = batch->dynamic_vaos.count; batch->dynamic_vaos.count += GPU_BATCH_VAO_DYN_ALLOC_COUNT; - batch->dynamic_vaos.interfaces = MEM_recallocN((void *)batch->dynamic_vaos.interfaces, - sizeof(GPUShaderInterface *) * - batch->dynamic_vaos.count); - batch->dynamic_vaos.vao_ids = MEM_recallocN(batch->dynamic_vaos.vao_ids, - sizeof(GLuint) * batch->dynamic_vaos.count); + batch->dynamic_vaos.interfaces = (const GPUShaderInterface **)MEM_recallocN( + (void *)batch->dynamic_vaos.interfaces, + sizeof(GPUShaderInterface *) * batch->dynamic_vaos.count); + batch->dynamic_vaos.vao_ids = (GLuint *)MEM_recallocN( + batch->dynamic_vaos.vao_ids, sizeof(GLuint) * batch->dynamic_vaos.count); } batch->dynamic_vaos.interfaces[i] = batch->interface; batch->dynamic_vaos.vao_ids[i] = new_vao = GPU_vao_alloc(); @@ -839,7 +839,7 @@ struct GPUDrawList { GPUDrawList *GPU_draw_list_create(int length) { - GPUDrawList *list = MEM_callocN(sizeof(GPUDrawList), "GPUDrawList"); + GPUDrawList *list = (GPUDrawList *)MEM_callocN(sizeof(GPUDrawList), "GPUDrawList"); /* Alloc the biggest possible command list which is indexed. */ list->buffer_size = sizeof(GPUDrawCommandIndexed) * length; if (USE_MULTI_DRAW_INDIRECT) { @@ -848,7 +848,7 @@ GPUDrawList *GPU_draw_list_create(int length) glBufferData(GL_DRAW_INDIRECT_BUFFER, list->buffer_size, NULL, GL_DYNAMIC_DRAW); } else { - list->commands = MEM_mallocN(list->buffer_size, "GPUDrawList data"); + list->commands = (GPUDrawCommand *)MEM_mallocN(list->buffer_size, "GPUDrawList data"); } return list; } @@ -880,7 +880,7 @@ void GPU_draw_list_init(GPUDrawList *list, GPUBatch *batch) list->cmd_offset = 0; } GLenum flags = GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_FLUSH_EXPLICIT_BIT; - list->commands = glMapBufferRange( + list->commands = (GPUDrawCommand *)glMapBufferRange( GL_DRAW_INDIRECT_BUFFER, list->cmd_offset, list->buffer_size - list->cmd_offset, flags); } } diff --git a/source/blender/gpu/intern/gpu_codegen.c b/source/blender/gpu/intern/gpu_codegen.c index 0a1d6f560b6..65c9a27e1fe 100644 --- a/source/blender/gpu/intern/gpu_codegen.c +++ b/source/blender/gpu/intern/gpu_codegen.c @@ -41,7 +41,6 @@ #include "BKE_material.h" #include "GPU_extensions.h" -#include "GPU_glew.h" #include "GPU_material.h" #include "GPU_shader.h" #include "GPU_uniformbuffer.h" @@ -282,18 +281,15 @@ static const char *gpu_builtin_name(eGPUBuiltin builtin) static void codegen_set_unique_ids(GPUNodeGraph *graph) { - GPUNode *node; - GPUInput *input; - GPUOutput *output; int id = 1; - for (node = graph->nodes.first; node; node = node->next) { - for (input = node->inputs.first; input; input = input->next) { + LISTBASE_FOREACH (GPUNode *, node, &graph->nodes) { + LISTBASE_FOREACH (GPUInput *, input, &node->inputs) { /* set id for unique names of uniform variables */ input->id = id++; } - for (output = node->outputs.first; output; output = output->next) { + LISTBASE_FOREACH (GPUOutput *, output, &node->outputs) { /* set id for unique names of tmp variables storing output */ output->id = id++; } @@ -307,8 +303,6 @@ static int codegen_process_uniforms_functions(GPUMaterial *material, DynStr *ds, GPUNodeGraph *graph) { - GPUNode *node; - GPUInput *input; const char *name; int builtins = 0; ListBase ubo_inputs = {NULL, NULL}; @@ -339,8 +333,9 @@ static int codegen_process_uniforms_functions(GPUMaterial *material, } /* Print other uniforms */ - for (node = graph->nodes.first; node; node = node->next) { - for (input = node->inputs.first; input; input = input->next) { + + LISTBASE_FOREACH (GPUNode *, node, &graph->nodes) { + LISTBASE_FOREACH (GPUInput *, input, &node->inputs) { if (input->source == GPU_SOURCE_BUILTIN) { /* only define each builtin uniform/varying once */ if (!(builtins & input->builtin)) { @@ -382,7 +377,7 @@ static int codegen_process_uniforms_functions(GPUMaterial *material, BLI_dynstr_appendf(ds, "\nlayout (std140) uniform %s {\n", GPU_UBO_BLOCK_NAME); LISTBASE_FOREACH (LinkData *, link, &ubo_inputs) { - input = link->data; + GPUInput *input = (GPUInput *)(link->data); BLI_dynstr_appendf(ds, "\t%s unf%d;\n", gpu_data_type_to_string(input->type), input->id); } BLI_dynstr_append(ds, "};\n"); @@ -396,12 +391,9 @@ static int codegen_process_uniforms_functions(GPUMaterial *material, static void codegen_declare_tmps(DynStr *ds, GPUNodeGraph *graph) { - GPUNode *node; - GPUOutput *output; - - for (node = graph->nodes.first; node; node = node->next) { + LISTBASE_FOREACH (GPUNode *, node, &graph->nodes) { /* declare temporary variables for node output storage */ - for (output = node->outputs.first; output; output = output->next) { + LISTBASE_FOREACH (GPUOutput *, output, &node->outputs) { if (output->type == GPU_CLOSURE) { BLI_dynstr_appendf(ds, "\tClosure tmp%d;\n", output->id); } @@ -410,20 +402,15 @@ static void codegen_declare_tmps(DynStr *ds, GPUNodeGraph *graph) } } } - BLI_dynstr_append(ds, "\n"); } static void codegen_call_functions(DynStr *ds, GPUNodeGraph *graph, GPUOutput *finaloutput) { - GPUNode *node; - GPUInput *input; - GPUOutput *output; - - for (node = graph->nodes.first; node; node = node->next) { + LISTBASE_FOREACH (GPUNode *, node, &graph->nodes) { BLI_dynstr_appendf(ds, "\t%s(", node->name); - for (input = node->inputs.first; input; input = input->next) { + LISTBASE_FOREACH (GPUInput *, input, &node->inputs) { if (input->source == GPU_SOURCE_TEX) { BLI_dynstr_append(ds, input->texture->sampler_name); } @@ -504,7 +491,7 @@ static void codegen_call_functions(DynStr *ds, GPUNodeGraph *graph, GPUOutput *f BLI_dynstr_append(ds, ", "); } - for (output = node->outputs.first; output; output = output->next) { + LISTBASE_FOREACH (GPUOutput *, output, &node->outputs) { BLI_dynstr_appendf(ds, "tmp%d", output->id); if (output->next) { BLI_dynstr_append(ds, ", "); @@ -662,8 +649,6 @@ static const char *attr_prefix_get(CustomDataType type) static char *code_generate_vertex(GPUNodeGraph *graph, const char *vert_code, bool use_geom) { DynStr *ds = BLI_dynstr_new(); - GPUNode *node; - GPUInput *input; char *code; int builtins = 0; @@ -708,8 +693,8 @@ static char *code_generate_vertex(GPUNodeGraph *graph, const char *vert_code, bo use_geom ? "g" : ""); } - for (node = graph->nodes.first; node; node = node->next) { - for (input = node->inputs.first; input; input = input->next) { + LISTBASE_FOREACH (GPUNode *, node, &graph->nodes) { + LISTBASE_FOREACH (GPUInput *, input, &node->inputs) { if (input->source == GPU_SOURCE_BUILTIN) { builtins |= input->builtin; } @@ -883,8 +868,6 @@ static char *code_generate_geometry(GPUNodeGraph *graph, const char *defines) { DynStr *ds = BLI_dynstr_new(); - GPUNode *node; - GPUInput *input; char *code; int builtins = 0; @@ -896,15 +879,15 @@ static char *code_generate_geometry(GPUNodeGraph *graph, BLI_dynstr_append(ds, "void calc_barycentric_distances(vec3 pos0, vec3 pos1, vec3 pos2);\n"); BLI_dynstr_append(ds, "#define USE_ATTR\n"); - /* Generate varying declarations. */ - for (node = graph->nodes.first; node; node = node->next) { - for (input = node->inputs.first; input; input = input->next) { + LISTBASE_FOREACH (GPUNode *, node, &graph->nodes) { + LISTBASE_FOREACH (GPUInput *, input, &node->inputs) { if (input->source == GPU_SOURCE_BUILTIN) { builtins |= input->builtin; } } } + /* Generate varying declarations. */ LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &graph->attributes) { BLI_dynstr_appendf(ds, "in %s var%dg[];\n", gpu_data_type_to_string(attr->gputype), attr->id); BLI_dynstr_appendf(ds, "out %s var%d;\n", gpu_data_type_to_string(attr->gputype), attr->id); diff --git a/source/blender/gpu/intern/gpu_codegen.h b/source/blender/gpu/intern/gpu_codegen.h index ce20f495ba3..18ff4c3241d 100644 --- a/source/blender/gpu/intern/gpu_codegen.h +++ b/source/blender/gpu/intern/gpu_codegen.h @@ -26,6 +26,10 @@ #ifndef __GPU_CODEGEN_H__ #define __GPU_CODEGEN_H__ +#ifdef __cplusplus +extern "C" { +#endif + struct GPUMaterial; struct GPUNodeGraph; struct GPUOutput; @@ -68,4 +72,8 @@ void GPU_pass_release(GPUPass *pass); void gpu_codegen_init(void); void gpu_codegen_exit(void); +#ifdef __cplusplus +} +#endif + #endif /* __GPU_CODEGEN_H__ */ diff --git a/source/blender/gpu/intern/gpu_context.cpp b/source/blender/gpu/intern/gpu_context.cc index 0b9104e5349..0b9104e5349 100644 --- a/source/blender/gpu/intern/gpu_context.cpp +++ b/source/blender/gpu/intern/gpu_context.cc diff --git a/source/blender/gpu/intern/gpu_debug.c b/source/blender/gpu/intern/gpu_debug.cc index f7d6236071d..f7d6236071d 100644 --- a/source/blender/gpu/intern/gpu_debug.c +++ b/source/blender/gpu/intern/gpu_debug.cc diff --git a/source/blender/gpu/intern/gpu_element.c b/source/blender/gpu/intern/gpu_element.cc index 036588b4a48..9f104ab3fec 100644 --- a/source/blender/gpu/intern/gpu_element.c +++ b/source/blender/gpu/intern/gpu_element.cc @@ -26,6 +26,7 @@ #include "MEM_guardedalloc.h" #include "GPU_element.h" +#include "GPU_glew.h" #include "gpu_context_private.h" @@ -37,21 +38,18 @@ static GLenum convert_index_type_to_gl(GPUIndexBufType type) { - static const GLenum table[] = { - [GPU_INDEX_U16] = GL_UNSIGNED_SHORT, - [GPU_INDEX_U32] = GL_UNSIGNED_INT, - }; - return table[type]; +#if GPU_TRACK_INDEX_RANGE + return (type == GPU_INDEX_U32) ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT; +#else + return GL_UNSIGNED_INT; +#endif } uint GPU_indexbuf_size_get(const GPUIndexBuf *elem) { #if GPU_TRACK_INDEX_RANGE - static const uint table[] = { - [GPU_INDEX_U16] = sizeof(GLushort), - [GPU_INDEX_U32] = sizeof(GLuint), - }; - return elem->index_len * table[elem->index_type]; + return elem->index_len * + ((elem->index_type == GPU_INDEX_U32) ? sizeof(GLuint) : sizeof(GLshort)); #else return elem->index_len * sizeof(GLuint); #endif @@ -86,7 +84,7 @@ void GPU_indexbuf_init_ex(GPUIndexBufBuilder *builder, builder->max_index_len = index_len; builder->index_len = 0; // start empty builder->prim_type = prim_type; - builder->data = MEM_callocN(builder->max_index_len * sizeof(uint), "GPUIndexBuf data"); + builder->data = (uint *)MEM_callocN(builder->max_index_len * sizeof(uint), "GPUIndexBuf data"); } void GPU_indexbuf_init(GPUIndexBufBuilder *builder, @@ -241,7 +239,7 @@ void GPU_indexbuf_set_tri_restart(GPUIndexBufBuilder *builder, uint elem) GPUIndexBuf *GPU_indexbuf_create_subrange(GPUIndexBuf *elem_src, uint start, uint length) { - GPUIndexBuf *elem = MEM_callocN(sizeof(GPUIndexBuf), "GPUIndexBuf"); + GPUIndexBuf *elem = (GPUIndexBuf *)MEM_callocN(sizeof(GPUIndexBuf), "GPUIndexBuf"); GPU_indexbuf_create_subrange_in_place(elem, elem_src, start, length); return elem; } @@ -331,7 +329,7 @@ static void squeeze_indices_short(GPUIndexBufBuilder *builder, GPUIndexBuf *GPU_indexbuf_build(GPUIndexBufBuilder *builder) { - GPUIndexBuf *elem = MEM_callocN(sizeof(GPUIndexBuf), "GPUIndexBuf"); + GPUIndexBuf *elem = (GPUIndexBuf *)MEM_callocN(sizeof(GPUIndexBuf), "GPUIndexBuf"); GPU_indexbuf_build_in_place(builder, elem); return elem; } diff --git a/source/blender/gpu/intern/gpu_extensions.c b/source/blender/gpu/intern/gpu_extensions.cc index 9aa3becef1d..a9ab25cbb41 100644 --- a/source/blender/gpu/intern/gpu_extensions.c +++ b/source/blender/gpu/intern/gpu_extensions.cc @@ -104,7 +104,8 @@ static struct GPUGlobal { static void gpu_detect_mip_render_workaround(void) { int cube_size = 2; - float *source_pix = MEM_callocN(sizeof(float) * 4 * 6 * cube_size * cube_size, __func__); + float *source_pix = (float *)MEM_callocN(sizeof(float) * 4 * 6 * cube_size * cube_size, + __func__); float clear_color[4] = {1.0f, 0.5f, 0.0f, 0.0f}; GPUTexture *tex = GPU_texture_create_cube(cube_size, GPU_RGBA16F, source_pix, NULL); @@ -123,7 +124,7 @@ static void gpu_detect_mip_render_workaround(void) GPU_framebuffer_restore(); GPU_framebuffer_free(fb); - float *data = GPU_texture_read(tex, GPU_DATA_FLOAT, 1); + float *data = (float *)GPU_texture_read(tex, GPU_DATA_FLOAT, 1); GG.mip_render_workaround = !equals_v4v4(clear_color, data); MEM_freeN(data); @@ -311,7 +312,7 @@ void gpu_extensions_init(void) if (GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_WIN, GPU_DRIVER_OFFICIAL)) { /* Limit this fix to older hardware with GL < 4.5. This means Broadwell GPUs are * covered since they only support GL 4.4 on windows. - * This fixes some issues with workbench antialiasing on Win + Intel GPU. (see T76273) */ + * This fixes some issues with workbench anti-aliasing on Win + Intel GPU. (see T76273) */ if (!GLEW_VERSION_4_5) { GG.texture_copy_workaround = true; } diff --git a/source/blender/gpu/intern/gpu_framebuffer.c b/source/blender/gpu/intern/gpu_framebuffer.cc index 77abb786117..3829573cde1 100644 --- a/source/blender/gpu/intern/gpu_framebuffer.c +++ b/source/blender/gpu/intern/gpu_framebuffer.cc @@ -53,6 +53,10 @@ typedef enum { GPU_FB_MAX_ATTACHEMENT, } GPUAttachmentType; +#define FOREACH_ATTACHMENT_RANGE(att, _start, _end) \ + for (GPUAttachmentType att = static_cast<GPUAttachmentType>(_start); att < _end; \ + att = static_cast<GPUAttachmentType>(att + 1)) + #define GPU_FB_MAX_COLOR_ATTACHMENT (GPU_FB_MAX_ATTACHEMENT - GPU_FB_COLOR_ATTACHMENT0) #define GPU_FB_DIRTY_DRAWBUFFER (1 << 15) @@ -74,17 +78,25 @@ struct GPUFrameBuffer { static GLenum convert_attachment_type_to_gl(GPUAttachmentType type) { - static const GLenum table[] = { - [GPU_FB_DEPTH_ATTACHMENT] = GL_DEPTH_ATTACHMENT, - [GPU_FB_DEPTH_STENCIL_ATTACHMENT] = GL_DEPTH_STENCIL_ATTACHMENT, - [GPU_FB_COLOR_ATTACHMENT0] = GL_COLOR_ATTACHMENT0, - [GPU_FB_COLOR_ATTACHMENT1] = GL_COLOR_ATTACHMENT1, - [GPU_FB_COLOR_ATTACHMENT2] = GL_COLOR_ATTACHMENT2, - [GPU_FB_COLOR_ATTACHMENT3] = GL_COLOR_ATTACHMENT3, - [GPU_FB_COLOR_ATTACHMENT4] = GL_COLOR_ATTACHMENT4, - [GPU_FB_COLOR_ATTACHMENT5] = GL_COLOR_ATTACHMENT5, - }; - return table[type]; +#define ATTACHMENT(type) \ + case GPU_FB_##type: { \ + return GL_##type; \ + } \ + ((void)0) + + switch (type) { + ATTACHMENT(DEPTH_ATTACHMENT); + ATTACHMENT(DEPTH_STENCIL_ATTACHMENT); + ATTACHMENT(COLOR_ATTACHMENT0); + ATTACHMENT(COLOR_ATTACHMENT1); + ATTACHMENT(COLOR_ATTACHMENT2); + ATTACHMENT(COLOR_ATTACHMENT3); + ATTACHMENT(COLOR_ATTACHMENT4); + ATTACHMENT(COLOR_ATTACHMENT5); + default: + BLI_assert(0); + return GL_COLOR_ATTACHMENT0; + } } static GPUAttachmentType attachment_type_from_tex(GPUTexture *tex, int slot) @@ -98,7 +110,7 @@ static GPUAttachmentType attachment_type_from_tex(GPUTexture *tex, int slot) case GPU_DEPTH32F_STENCIL8: return GPU_FB_DEPTH_STENCIL_ATTACHMENT; default: - return GPU_FB_COLOR_ATTACHMENT0 + slot; + return static_cast<GPUAttachmentType>(GPU_FB_COLOR_ATTACHMENT0 + slot); } } @@ -198,7 +210,7 @@ GPUFrameBuffer *GPU_framebuffer_create(void) { /* We generate the FB object later at first use in order to * create the framebuffer in the right opengl context. */ - return MEM_callocN(sizeof(GPUFrameBuffer), "GPUFrameBuffer"); + return (GPUFrameBuffer *)MEM_callocN(sizeof(GPUFrameBuffer), "GPUFrameBuffer"); } static void gpu_framebuffer_init(GPUFrameBuffer *fb) @@ -210,7 +222,8 @@ static void gpu_framebuffer_init(GPUFrameBuffer *fb) void GPU_framebuffer_free(GPUFrameBuffer *fb) { - for (GPUAttachmentType type = 0; type < GPU_FB_MAX_ATTACHEMENT; type++) { + for (int i_type = 0; i_type < GPU_FB_MAX_ATTACHEMENT; i_type++) { + GPUAttachmentType type = static_cast<GPUAttachmentType>(i_type); if (fb->attachments[type].tex != NULL) { GPU_framebuffer_texture_detach(fb, fb->attachments[type].tex); } @@ -304,7 +317,7 @@ void GPU_framebuffer_texture_detach_slot(GPUFrameBuffer *fb, GPUTexture *tex, in void GPU_framebuffer_texture_detach(GPUFrameBuffer *fb, GPUTexture *tex) { - GPUAttachmentType type = GPU_texture_detach_framebuffer(tex, fb); + GPUAttachmentType type = (GPUAttachmentType)GPU_texture_detach_framebuffer(tex, fb); GPU_framebuffer_texture_detach_slot(fb, tex, type); } @@ -387,8 +400,8 @@ static void gpu_framebuffer_update_attachments(GPUFrameBuffer *fb) BLI_assert(GPU_framebuffer_active_get() == fb); /* Update attachments */ - for (GPUAttachmentType type = 0; type < GPU_FB_MAX_ATTACHEMENT; type++) { - + FOREACH_ATTACHMENT_RANGE(type, 0, GPU_FB_MAX_ATTACHEMENT) + { if (type >= GPU_FB_COLOR_ATTACHMENT0) { if (fb->attachments[type].tex) { gl_attachments[numslots] = convert_attachment_type_to_gl(type); @@ -438,7 +451,8 @@ static void gpu_framebuffer_update_attachments_and_fill_empty_slots(GPUFrameBuff BLI_assert(GPU_framebuffer_active_get() == fb); /* Update attachments */ - for (GPUAttachmentType type = GPU_FB_MAX_ATTACHEMENT; type--;) { + for (int i_type = GPU_FB_MAX_ATTACHEMENT; i_type >= 0; --i_type) { + GPUAttachmentType type = static_cast<GPUAttachmentType>(i_type); GPUTexture *tex = fb->attachments[type].tex; if (type >= GPU_FB_COLOR_ATTACHMENT0) { @@ -623,8 +637,9 @@ void GPU_framebuffer_multi_clear(GPUFrameBuffer *fb, const float (*clear_cols)[4 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - GPUAttachmentType type = GPU_FB_COLOR_ATTACHMENT0; - for (int i = 0; type < GPU_FB_MAX_ATTACHEMENT; i++, type++) { + int i_type = GPU_FB_COLOR_ATTACHMENT0; + for (int i = 0; i_type < GPU_FB_MAX_ATTACHEMENT; i++, i_type++) { + GPUAttachmentType type = static_cast<GPUAttachmentType>(i_type); if (fb->attachments[type].tex != NULL) { glClearBufferfv(GL_COLOR, i, clear_cols[i]); } @@ -702,7 +717,8 @@ void GPU_framebuffer_read_color(GPUFrameBuffer *fb, void *data) { CHECK_FRAMEBUFFER_IS_BOUND(fb); - gpu_framebuffer_read_color_ex(x, y, w, h, channels, GL_COLOR_ATTACHMENT0 + slot, format, data); + gpu_framebuffer_read_color_ex( + x, y, w, h, channels, GL_COLOR_ATTACHMENT0 + slot, format, (float *)data); } /* read_slot and write_slot are only used for color buffers. */ @@ -815,7 +831,8 @@ void GPU_framebuffer_recursive_downsample(GPUFrameBuffer *fb, current_dim[0] = max_ii(current_dim[0] / 2, 1); current_dim[1] = max_ii(current_dim[1] / 2, 1); - for (GPUAttachmentType type = 0; type < GPU_FB_MAX_ATTACHEMENT; type++) { + for (int i_type = 0; i_type < GPU_FB_MAX_ATTACHEMENT; i_type++) { + GPUAttachmentType type = static_cast<GPUAttachmentType>(i_type); if (fb->attachments[type].tex != NULL) { /* Some Intel HDXXX have issue with rendering to a mipmap that is below * the texture GL_TEXTURE_MAX_LEVEL. So even if it not correct, in this case @@ -844,7 +861,8 @@ void GPU_framebuffer_recursive_downsample(GPUFrameBuffer *fb, } } - for (GPUAttachmentType type = 0; type < GPU_FB_MAX_ATTACHEMENT; type++) { + for (int i_type = 0; i_type < GPU_FB_MAX_ATTACHEMENT; i_type++) { + GPUAttachmentType type = static_cast<GPUAttachmentType>(i_type); if (fb->attachments[type].tex != NULL) { /* reset mipmap level range */ GPUTexture *tex = fb->attachments[type].tex; @@ -885,9 +903,11 @@ static GPUFrameBuffer *gpu_offscreen_fb_get(GPUOffScreen *ofs) for (int i = 0; i < MAX_CTX_FB_LEN; i++) { if (ofs->framebuffers[i].fb == NULL) { ofs->framebuffers[i].ctx = ctx; - GPU_framebuffer_ensure_config( - &ofs->framebuffers[i].fb, - {GPU_ATTACHMENT_TEXTURE(ofs->depth), GPU_ATTACHMENT_TEXTURE(ofs->color)}); + GPU_framebuffer_ensure_config(&ofs->framebuffers[i].fb, + { + GPU_ATTACHMENT_TEXTURE(ofs->depth), + GPU_ATTACHMENT_TEXTURE(ofs->color), + }); } if (ofs->framebuffers[i].ctx == ctx) { @@ -916,9 +936,7 @@ static GPUFrameBuffer *gpu_offscreen_fb_get(GPUOffScreen *ofs) GPUOffScreen *GPU_offscreen_create( int width, int height, bool depth, bool high_bitdepth, char err_out[256]) { - GPUOffScreen *ofs; - - ofs = MEM_callocN(sizeof(GPUOffScreen), "GPUOffScreen"); + GPUOffScreen *ofs = (GPUOffScreen *)MEM_callocN(sizeof(GPUOffScreen), __func__); /* Sometimes areas can have 0 height or width and this will * create a 1D texture which we don't want. */ @@ -975,7 +993,7 @@ void GPU_offscreen_free(GPUOffScreen *ofs) void GPU_offscreen_bind(GPUOffScreen *ofs, bool save) { if (save) { - gpuPushAttr(GPU_SCISSOR_BIT | GPU_VIEWPORT_BIT); + gpuPushAttr((eGPUAttrMask)(GPU_SCISSOR_BIT | GPU_VIEWPORT_BIT)); GPUFrameBuffer *fb = GPU_framebuffer_active_get(); gpuPushFrameBuffer(fb); } @@ -1023,14 +1041,15 @@ void GPU_offscreen_draw_to_screen(GPUOffScreen *ofs, int x, int y) glBindFramebuffer(GL_READ_FRAMEBUFFER, GPU_framebuffer_default()); } -void GPU_offscreen_read_pixels(GPUOffScreen *ofs, int type, void *pixels) +void GPU_offscreen_read_pixels(GPUOffScreen *ofs, eGPUDataFormat type, void *pixels) { const int w = GPU_texture_width(ofs->color); const int h = GPU_texture_height(ofs->color); - BLI_assert(type == GL_UNSIGNED_BYTE || type == GL_FLOAT); + BLI_assert(ELEM(type, GPU_DATA_UNSIGNED_BYTE, GL_FLOAT)); + GLenum gl_type = (type == GPU_DATA_FLOAT) ? GL_FLOAT : GL_UNSIGNED_BYTE; - glReadPixels(0, 0, w, h, GL_RGBA, type, pixels); + glReadPixels(0, 0, w, h, GL_RGBA, gl_type, pixels); } int GPU_offscreen_width(const GPUOffScreen *ofs) @@ -1077,8 +1096,7 @@ void GPU_clear(eGPUFrameBufferBits flags) void GPU_frontbuffer_read_pixels( int x, int y, int w, int h, int channels, eGPUDataFormat format, void *data) { - glReadBuffer(GL_FRONT); - gpu_framebuffer_read_color_ex(x, y, w, h, channels, GL_FRONT, format, data); + gpu_framebuffer_read_color_ex(x, y, w, h, channels, GL_FRONT, format, (float *)data); } /* For stereo rendering. */ diff --git a/source/blender/gpu/intern/gpu_immediate.c b/source/blender/gpu/intern/gpu_immediate.cc index 4f5cb3fcc91..4780b2dc7b3 100644 --- a/source/blender/gpu/intern/gpu_immediate.c +++ b/source/blender/gpu/intern/gpu_immediate.cc @@ -29,6 +29,7 @@ #include "GPU_attr_binding.h" #include "GPU_immediate.h" +#include "GPU_matrix.h" #include "GPU_texture.h" #include "gpu_attr_binding_private.h" @@ -40,10 +41,6 @@ #include <stdlib.h> #include <string.h> -/* necessary functions from matrix API */ -extern void GPU_matrix_bind(const GPUShaderInterface *); -extern bool GPU_matrix_dirty_get(void); - typedef struct ImmediateDrawBuffer { GLuint vbo_id; GLubyte *buffer_data; @@ -281,7 +278,7 @@ void immBegin(GPUPrimType prim_type, uint vertex_len) } #endif - active_buffer->buffer_data = glMapBufferRange( + active_buffer->buffer_data = (GLubyte *)glMapBufferRange( GL_ARRAY_BUFFER, active_buffer->buffer_offset, bytes_needed, diff --git a/source/blender/gpu/intern/gpu_node_graph.h b/source/blender/gpu/intern/gpu_node_graph.h index bf59b720cff..a133dd1ccdc 100644 --- a/source/blender/gpu/intern/gpu_node_graph.h +++ b/source/blender/gpu/intern/gpu_node_graph.h @@ -29,7 +29,6 @@ #include "DNA_customdata_types.h" #include "DNA_listBase.h" -#include "GPU_glew.h" #include "GPU_material.h" #include "GPU_shader.h" diff --git a/source/blender/gpu/intern/gpu_platform.c b/source/blender/gpu/intern/gpu_platform.cc index 5cabde61bc3..5cabde61bc3 100644 --- a/source/blender/gpu/intern/gpu_platform.c +++ b/source/blender/gpu/intern/gpu_platform.cc diff --git a/source/blender/gpu/intern/gpu_primitive.c b/source/blender/gpu/intern/gpu_primitive.c index 2285c1ab95b..3b11b38db87 100644 --- a/source/blender/gpu/intern/gpu_primitive.c +++ b/source/blender/gpu/intern/gpu_primitive.c @@ -26,35 +26,6 @@ #include "GPU_primitive.h" #include "gpu_primitive_private.h" -GPUPrimClass GPU_primtype_class(GPUPrimType prim_type) -{ - static const GPUPrimClass classes[] = { - [GPU_PRIM_POINTS] = GPU_PRIM_CLASS_POINT, - [GPU_PRIM_LINES] = GPU_PRIM_CLASS_LINE, - [GPU_PRIM_LINE_STRIP] = GPU_PRIM_CLASS_LINE, - [GPU_PRIM_LINE_LOOP] = GPU_PRIM_CLASS_LINE, - [GPU_PRIM_TRIS] = GPU_PRIM_CLASS_SURFACE, - [GPU_PRIM_TRI_STRIP] = GPU_PRIM_CLASS_SURFACE, - [GPU_PRIM_TRI_FAN] = GPU_PRIM_CLASS_SURFACE, - - [GPU_PRIM_LINES_ADJ] = GPU_PRIM_CLASS_LINE, - [GPU_PRIM_LINE_STRIP_ADJ] = GPU_PRIM_CLASS_LINE, - [GPU_PRIM_TRIS_ADJ] = GPU_PRIM_CLASS_SURFACE, - - [GPU_PRIM_NONE] = GPU_PRIM_CLASS_NONE, - }; - - return classes[prim_type]; -} - -bool GPU_primtype_belongs_to_class(GPUPrimType prim_type, GPUPrimClass prim_class) -{ - if (prim_class == GPU_PRIM_CLASS_NONE && prim_type == GPU_PRIM_NONE) { - return true; - } - return prim_class & GPU_primtype_class(prim_type); -} - GLenum convert_prim_type_to_gl(GPUPrimType prim_type) { #if TRUST_NO_ONE diff --git a/source/blender/gpu/intern/gpu_primitive_private.h b/source/blender/gpu/intern/gpu_primitive_private.h index abefa6abd20..b7d7b262128 100644 --- a/source/blender/gpu/intern/gpu_primitive_private.h +++ b/source/blender/gpu/intern/gpu_primitive_private.h @@ -26,6 +26,15 @@ #ifndef __GPU_PRIMITIVE_PRIVATE_H__ #define __GPU_PRIMITIVE_PRIVATE_H__ +#ifdef __cplusplus +extern "C" { +#endif + +/* TODO(fclem) move to OGL backend */ GLenum convert_prim_type_to_gl(GPUPrimType); +#ifdef __cplusplus +} +#endif + #endif /* __GPU_PRIMITIVE_PRIVATE_H__ */ diff --git a/source/blender/gpu/intern/gpu_private.h b/source/blender/gpu/intern/gpu_private.h index 7846bff87f4..ed729dd399c 100644 --- a/source/blender/gpu/intern/gpu_private.h +++ b/source/blender/gpu/intern/gpu_private.h @@ -21,6 +21,10 @@ #ifndef __GPU_PRIVATE_H__ #define __GPU_PRIVATE_H__ +#ifdef __cplusplus +extern "C" { +#endif + /* call this before running any of the functions below */ void gpu_platform_init(void); void gpu_platform_exit(void); @@ -41,4 +45,8 @@ void gpu_framebuffer_module_exit(void); void gpu_pbvh_init(void); void gpu_pbvh_exit(void); +#ifdef __cplusplus +} +#endif + #endif /* __GPU_PRIVATE_H__ */ diff --git a/source/blender/gpu/intern/gpu_select.c b/source/blender/gpu/intern/gpu_select.c index 5766a176a96..c069cbe012f 100644 --- a/source/blender/gpu/intern/gpu_select.c +++ b/source/blender/gpu/intern/gpu_select.c @@ -26,7 +26,6 @@ #include <stdlib.h> #include <string.h> -#include "GPU_glew.h" #include "GPU_select.h" #include "MEM_guardedalloc.h" diff --git a/source/blender/gpu/intern/gpu_shader_private.h b/source/blender/gpu/intern/gpu_shader_private.h index e4443e79a8d..5a9a74cdfcc 100644 --- a/source/blender/gpu/intern/gpu_shader_private.h +++ b/source/blender/gpu/intern/gpu_shader_private.h @@ -21,9 +21,12 @@ #ifndef __GPU_SHADER_PRIVATE_H__ #define __GPU_SHADER_PRIVATE_H__ -#include "GPU_glew.h" #include "GPU_shader_interface.h" +#ifdef __cplusplus +extern "C" { +#endif + struct GPUShader { /** Handle for full program (links shader stages below). */ GLuint program; @@ -47,4 +50,8 @@ struct GPUShader { /* XXX do not use it. Special hack to use OCIO with batch API. */ void immGetProgram(GLuint *program, GPUShaderInterface **shaderface); +#ifdef __cplusplus +} +#endif + #endif /* __GPU_SHADER_PRIVATE_H__ */ diff --git a/source/blender/gpu/intern/gpu_texture.c b/source/blender/gpu/intern/gpu_texture.c index 4c847236f09..88424cb4cec 100644 --- a/source/blender/gpu/intern/gpu_texture.c +++ b/source/blender/gpu/intern/gpu_texture.c @@ -45,6 +45,16 @@ #include "gpu_context_private.h" +#define WARN_NOT_BOUND(_tex) \ + { \ + if (_tex->number == -1) { \ + fprintf(stderr, "Warning : Trying to set parameter on a texture not bound.\n"); \ + BLI_assert(0); \ + return; \ + } \ + } \ + ((void)0) + static struct GPUTextureGlobal { /** Texture used in place of invalid textures (not loaded correctly, missing). */ GPUTexture *invalid_tex_1D; @@ -1176,8 +1186,27 @@ GPUTexture *GPU_texture_create_buffer(eGPUTextureFormat tex_format, const GLuint return tex; } -GPUTexture *GPU_texture_from_bindcode(int textarget, int bindcode) +static GLenum convert_target_to_gl(eGPUTextureTarget target) +{ + switch (target) { + case TEXTARGET_2D: + return GL_TEXTURE_2D; + case TEXTARGET_CUBE_MAP: + return GL_TEXTURE_CUBE_MAP; + case TEXTARGET_2D_ARRAY: + return GL_TEXTURE_2D_ARRAY; + case TEXTARGET_TILE_MAPPING: + return GL_TEXTURE_1D_ARRAY; + default: + BLI_assert(0); + return GL_TEXTURE_2D; + } +} + +GPUTexture *GPU_texture_from_bindcode(eGPUTextureTarget target, int bindcode) { + GLenum textarget = convert_target_to_gl(target); + GPUTexture *tex = MEM_callocN(sizeof(GPUTexture), "GPUTexture"); tex->bindcode = bindcode; tex->refcount = 1; @@ -1468,7 +1497,8 @@ void GPU_texture_update_sub(GPUTexture *tex, GLenum data_format = gpu_get_gl_dataformat(tex->format, &tex->format_flag); GLenum data_type = gpu_get_gl_datatype(gpu_data_format); - glBindTexture(tex->target, tex->bindcode); + WARN_NOT_BOUND(tex); + switch (tex->target) { case GL_TEXTURE_1D: glTexSubImage1D(tex->target, 0, offset_x, width, data_format, data_type, pixels); @@ -1496,8 +1526,6 @@ void GPU_texture_update_sub(GPUTexture *tex, default: BLI_assert(!"tex->target mode not supported"); } - - glBindTexture(tex->target, 0); } void *GPU_texture_read(GPUTexture *tex, eGPUDataFormat gpu_data_format, int miplvl) @@ -1791,16 +1819,6 @@ void GPU_texture_unbind_all(void) glActiveTexture(GL_TEXTURE0); } -#define WARN_NOT_BOUND(_tex) \ - { \ - if (_tex->number == -1) { \ - fprintf(stderr, "Warning : Trying to set parameter on a texture not bound.\n"); \ - BLI_assert(0); \ - return; \ - } \ - } \ - ((void)0) - void GPU_texture_generate_mipmap(GPUTexture *tex) { WARN_NOT_BOUND(tex); diff --git a/source/blender/gpu/intern/gpu_draw.c b/source/blender/gpu/intern/gpu_texture_image.cc index e5f49555265..5b53ba0544b 100644 --- a/source/blender/gpu/intern/gpu_draw.c +++ b/source/blender/gpu/intern/gpu_texture_image.cc @@ -63,6 +63,20 @@ static void gpu_free_image(Image *ima, const bool immediate); static void gpu_free_unused_buffers(void); +static void gpu_create_gl_tex_compressed(unsigned int *bind, + eGPUTextureTarget textarget, + Image *ima, + ImBuf *ibuf); +static void gpu_create_gl_tex(uint *bind, + uint *rect, + float *frect, + int rectw, + int recth, + eGPUTextureTarget textarget, + bool mipmap, + bool half_float, + bool use_srgb, + Image *ima); //* Checking powers of two for images since OpenGL ES requires it */ #ifdef WITH_DDS @@ -182,29 +196,20 @@ float GPU_get_anisotropic(void) return GTS.anisotropic; } -/* Set OpenGL state for an MTFace */ - -static GPUTexture **gpu_get_image_gputexture(Image *ima, GLenum textarget, const int multiview_eye) +static GPUTexture **gpu_get_image_gputexture(Image *ima, int textarget, const int multiview_eye) { - if (textarget == GL_TEXTURE_2D) { - return &(ima->gputexture[TEXTARGET_TEXTURE_2D][multiview_eye]); - } - else if (textarget == GL_TEXTURE_CUBE_MAP) { - return &(ima->gputexture[TEXTARGET_TEXTURE_CUBE_MAP][multiview_eye]); - } - else if (textarget == GL_TEXTURE_2D_ARRAY) { - return &(ima->gputexture[TEXTARGET_TEXTURE_2D_ARRAY][multiview_eye]); - } - else if (textarget == GL_TEXTURE_1D_ARRAY) { - return &(ima->gputexture[TEXTARGET_TEXTURE_TILE_MAPPING][multiview_eye]); - } + const bool in_range = (textarget >= 0) && (textarget < TEXTARGET_COUNT); + BLI_assert(in_range); + if (in_range) { + return &(ima->gputexture[textarget][multiview_eye]); + } return NULL; } static uint gpu_texture_create_tile_mapping(Image *ima, const int multiview_eye) { - GPUTexture *tilearray = ima->gputexture[TEXTARGET_TEXTURE_2D_ARRAY][multiview_eye]; + GPUTexture *tilearray = ima->gputexture[TEXTARGET_2D_ARRAY][multiview_eye]; if (tilearray == NULL) { return 0; @@ -213,7 +218,7 @@ static uint gpu_texture_create_tile_mapping(Image *ima, const int multiview_eye) float array_w = GPU_texture_width(tilearray); float array_h = GPU_texture_height(tilearray); - ImageTile *last_tile = ima->tiles.last; + ImageTile *last_tile = (ImageTile *)ima->tiles.last; /* Tiles are sorted by number. */ int max_tile = last_tile->tile_number - 1001; @@ -223,7 +228,7 @@ static uint gpu_texture_create_tile_mapping(Image *ima, const int multiview_eye) glBindTexture(GL_TEXTURE_1D_ARRAY, bindcode); int width = max_tile + 1; - float *data = MEM_callocN(width * 8 * sizeof(float), __func__); + float *data = (float *)MEM_callocN(width * 8 * sizeof(float), __func__); for (int i = 0; i < width; i++) { data[4 * i] = -1.0f; } @@ -257,8 +262,8 @@ typedef struct PackTile { static int compare_packtile(const void *a, const void *b) { - const PackTile *tile_a = a; - const PackTile *tile_b = b; + const PackTile *tile_a = (const PackTile *)a; + const PackTile *tile_b = (const PackTile *)b; return tile_a->pack_score < tile_b->pack_score; } @@ -276,7 +281,7 @@ static uint gpu_texture_create_tile_array(Image *ima, ImBuf *main_ibuf) ImBuf *ibuf = BKE_image_acquire_ibuf(ima, &iuser, NULL); if (ibuf) { - PackTile *packtile = MEM_callocN(sizeof(PackTile), __func__); + PackTile *packtile = (PackTile *)MEM_callocN(sizeof(PackTile), __func__); packtile->tile = tile; packtile->boxpack.w = ibuf->x; packtile->boxpack.h = ibuf->y; @@ -380,7 +385,7 @@ static uint gpu_texture_create_tile_array(Image *ima, ImBuf *main_ibuf) const bool store_premultiplied = ima->alpha_mode != IMA_ALPHA_STRAIGHT; if (ibuf->channels != 4 || !store_premultiplied) { - rect_float = MEM_mallocN(sizeof(float) * 4 * ibuf->x * ibuf->y, __func__); + rect_float = (float *)MEM_mallocN(sizeof(float) * 4 * ibuf->x * ibuf->y, __func__); IMB_colormanagement_imbuf_to_float_texture( rect_float, 0, 0, ibuf->x, ibuf->y, ibuf, store_premultiplied); } @@ -412,7 +417,7 @@ static uint gpu_texture_create_tile_array(Image *ima, ImBuf *main_ibuf) unsigned int *rect = ibuf->rect; if (!IMB_colormanagement_space_is_data(ibuf->rect_colorspace)) { - rect = MEM_mallocN(sizeof(uchar) * 4 * ibuf->x * ibuf->y, __func__); + rect = (uint *)MEM_mallocN(sizeof(uchar) * 4 * ibuf->x * ibuf->y, __func__); IMB_colormanagement_imbuf_to_byte_texture((uchar *)rect, 0, 0, @@ -465,7 +470,7 @@ static uint gpu_texture_create_tile_array(Image *ima, ImBuf *main_ibuf) return bindcode; } -static uint gpu_texture_create_from_ibuf(Image *ima, ImBuf *ibuf, int textarget) +static uint gpu_texture_create_from_ibuf(Image *ima, ImBuf *ibuf, eGPUTextureTarget textarget) { uint bindcode = 0; const bool mipmap = GPU_get_mipmap(); @@ -474,9 +479,11 @@ static uint gpu_texture_create_from_ibuf(Image *ima, ImBuf *ibuf, int textarget) #ifdef WITH_DDS if (ibuf->ftype == IMB_FTYPE_DDS) { /* DDS is loaded directly in compressed form. */ - GPU_create_gl_tex_compressed(&bindcode, textarget, ima, ibuf); + gpu_create_gl_tex_compressed(&bindcode, textarget, ima, ibuf); return bindcode; } +#else + (void)gpu_create_gl_tex_compressed; #endif /* Regular uncompressed texture. */ @@ -495,7 +502,7 @@ static uint gpu_texture_create_from_ibuf(Image *ima, ImBuf *ibuf, int textarget) if (!IMB_colormanagement_space_is_data(ibuf->rect_colorspace)) { compress_as_srgb = !IMB_colormanagement_space_is_scene_linear(ibuf->rect_colorspace); - rect = MEM_mallocN(sizeof(uchar) * 4 * ibuf->x * ibuf->y, __func__); + rect = (uchar *)MEM_mallocN(sizeof(uchar) * 4 * ibuf->x * ibuf->y, __func__); if (rect == NULL) { return bindcode; } @@ -517,7 +524,7 @@ static uint gpu_texture_create_from_ibuf(Image *ima, ImBuf *ibuf, int textarget) const bool store_premultiplied = ima ? (ima->alpha_mode != IMA_ALPHA_STRAIGHT) : false; if (ibuf->channels != 4 || !store_premultiplied) { - rect_float = MEM_mallocN(sizeof(float) * 4 * ibuf->x * ibuf->y, __func__); + rect_float = (float *)MEM_mallocN(sizeof(float) * 4 * ibuf->x * ibuf->y, __func__); if (rect_float == NULL) { return bindcode; } @@ -527,7 +534,7 @@ static uint gpu_texture_create_from_ibuf(Image *ima, ImBuf *ibuf, int textarget) } /* Create OpenGL texture. */ - GPU_create_gl_tex(&bindcode, + gpu_create_gl_tex(&bindcode, (uint *)rect, rect_float, ibuf->x, @@ -551,33 +558,25 @@ static uint gpu_texture_create_from_ibuf(Image *ima, ImBuf *ibuf, int textarget) static GPUTexture **gpu_get_movieclip_gputexture(MovieClip *clip, MovieClipUser *cuser, - GLenum textarget) + eGPUTextureTarget textarget) { - MovieClip_RuntimeGPUTexture *tex; - for (tex = clip->runtime.gputextures.first; tex; tex = tex->next) { + LISTBASE_FOREACH (MovieClip_RuntimeGPUTexture *, tex, &clip->runtime.gputextures) { if (memcmp(&tex->user, cuser, sizeof(MovieClipUser)) == 0) { - break; - } - } - - if (tex == NULL) { - tex = MEM_mallocN(sizeof(MovieClip_RuntimeGPUTexture), __func__); + if (tex == NULL) { + tex = (MovieClip_RuntimeGPUTexture *)MEM_mallocN(sizeof(MovieClip_RuntimeGPUTexture), + __func__); - for (int i = 0; i < TEXTARGET_COUNT; i++) { - tex->gputexture[i] = NULL; - } + for (int i = 0; i < TEXTARGET_COUNT; i++) { + tex->gputexture[i] = NULL; + } - memcpy(&tex->user, cuser, sizeof(MovieClipUser)); - BLI_addtail(&clip->runtime.gputextures, tex); - } + memcpy(&tex->user, cuser, sizeof(MovieClipUser)); + BLI_addtail(&clip->runtime.gputextures, tex); + } - if (textarget == GL_TEXTURE_2D) { - return &tex->gputexture[TEXTARGET_TEXTURE_2D]; - } - else if (textarget == GL_TEXTURE_CUBE_MAP) { - return &tex->gputexture[TEXTARGET_TEXTURE_CUBE_MAP]; + return &tex->gputexture[textarget]; + } } - return NULL; } @@ -765,7 +764,7 @@ static void gpu_texture_update_from_ibuf( const bool compress_as_srgb = !IMB_colormanagement_space_is_scene_linear( ibuf->rect_colorspace); - rect = MEM_mallocN(sizeof(uchar) * 4 * w * h, __func__); + rect = (uchar *)MEM_mallocN(sizeof(uchar) * 4 * w * h, __func__); if (rect == NULL) { return; } @@ -785,7 +784,7 @@ static void gpu_texture_update_from_ibuf( const bool store_premultiplied = (ima->alpha_mode != IMA_ALPHA_STRAIGHT); if (ibuf->channels != 4 || scaled || !store_premultiplied) { - rect_float = MEM_mallocN(sizeof(float) * 4 * w * h, __func__); + rect_float = (float *)MEM_mallocN(sizeof(float) * 4 * w * h, __func__); if (rect_float == NULL) { return; } @@ -853,7 +852,10 @@ static void gpu_texture_update_from_ibuf( * * `iuser` and `ibuf` are mutual exclusive parameters. The caller can pass the `ibuf` when already * available. It is also required when requesting the GPUTexture for a render result. */ -GPUTexture *GPU_texture_from_blender(Image *ima, ImageUser *iuser, ImBuf *ibuf, int textarget) +GPUTexture *GPU_texture_from_blender(Image *ima, + ImageUser *iuser, + ImBuf *ibuf, + eGPUTextureTarget textarget) { #ifndef GPU_STANDALONE if (ima == NULL) { @@ -898,10 +900,10 @@ GPUTexture *GPU_texture_from_blender(Image *ima, ImageUser *iuser, ImBuf *ibuf, } } - if (textarget == GL_TEXTURE_2D_ARRAY) { + if (textarget == TEXTARGET_2D_ARRAY) { bindcode = gpu_texture_create_tile_array(ima, ibuf_intern); } - else if (textarget == GL_TEXTURE_1D_ARRAY) { + else if (textarget == TEXTARGET_TILE_MAPPING) { bindcode = gpu_texture_create_tile_mapping(ima, iuser ? iuser->multiview_eye : 0); } else { @@ -917,7 +919,7 @@ GPUTexture *GPU_texture_from_blender(Image *ima, ImageUser *iuser, ImBuf *ibuf, GPU_texture_orig_size_set(*tex, ibuf_intern->x, ibuf_intern->y); - if (textarget == GL_TEXTURE_1D_ARRAY) { + if (textarget == TEXTARGET_TILE_MAPPING) { /* Special for tile mapping. */ GPU_texture_mipmap_mode(*tex, false, false); } @@ -927,7 +929,9 @@ GPUTexture *GPU_texture_from_blender(Image *ima, ImageUser *iuser, ImBuf *ibuf, return NULL; } -GPUTexture *GPU_texture_from_movieclip(MovieClip *clip, MovieClipUser *cuser, int textarget) +GPUTexture *GPU_texture_from_movieclip(MovieClip *clip, + MovieClipUser *cuser, + eGPUTextureTarget textarget) { #ifndef GPU_STANDALONE if (clip == NULL) { @@ -965,7 +969,8 @@ void GPU_free_texture_movieclip(struct MovieClip *clip) const int MOVIECLIP_NUM_GPUTEXTURES = 1; while (BLI_listbase_count(&clip->runtime.gputextures) > MOVIECLIP_NUM_GPUTEXTURES) { - MovieClip_RuntimeGPUTexture *tex = BLI_pophead(&clip->runtime.gputextures); + MovieClip_RuntimeGPUTexture *tex = (MovieClip_RuntimeGPUTexture *)BLI_pophead( + &clip->runtime.gputextures); for (int i = 0; i < TEXTARGET_COUNT; i++) { /* free glsl image binding */ if (tex->gputexture[i]) { @@ -989,7 +994,7 @@ static void **gpu_gen_cube_map(uint *rect, float *frect, int rectw, int recth) } /* PosX, NegX, PosY, NegY, PosZ, NegZ */ - sides = MEM_mallocN(sizeof(void *) * 6, ""); + sides = (void **)MEM_mallocN(sizeof(void *) * 6, ""); for (int i = 0; i < 6; i++) { sides[i] = MEM_mallocN(block_size * w * h, ""); } @@ -1050,17 +1055,19 @@ static void gpu_del_cube_map(void **cube_map) } /* Image *ima can be NULL */ -void GPU_create_gl_tex(uint *bind, - uint *rect, - float *frect, - int rectw, - int recth, - int textarget, - bool mipmap, - bool half_float, - bool use_srgb, - Image *ima) +static void gpu_create_gl_tex(uint *bind, + uint *rect, + float *frect, + int rectw, + int recth, + eGPUTextureTarget target, + bool mipmap, + bool half_float, + bool use_srgb, + Image *ima) { + GLenum textarget = (target == TEXTARGET_2D) ? GL_TEXTURE_2D : GL_TEXTURE_CUBE_MAP; + ImBuf *ibuf = NULL; if (textarget == GL_TEXTURE_2D && is_over_resolution_limit(textarget, rectw, recth)) { @@ -1151,13 +1158,14 @@ void GPU_create_gl_tex(uint *bind, } /** - * GPU_upload_dxt_texture() assumes that the texture is already bound and ready to go. - * This is so the viewport and the BGE can share some code. * Returns false if the provided ImBuf doesn't have a supported DXT compression format */ -bool GPU_upload_dxt_texture(ImBuf *ibuf, bool use_srgb) +bool GPU_upload_dxt_texture(ImBuf *ibuf, bool use_srgb, uint *bindcode) { #ifdef WITH_DDS + glGenTextures(1, (GLuint *)bindcode); + glBindTexture(GL_TEXTURE_2D, *bindcode); + GLint format = 0; int blocksize, height, width, i, size, offset = 0; @@ -1181,6 +1189,7 @@ bool GPU_upload_dxt_texture(ImBuf *ibuf, bool use_srgb) if (format == 0) { fprintf(stderr, "Unable to find a suitable DXT compression, falling back to uncompressed\n"); + glDeleteTextures(1, (GLuint *)bindcode); return false; } @@ -1188,6 +1197,7 @@ bool GPU_upload_dxt_texture(ImBuf *ibuf, bool use_srgb) fprintf( stderr, "Unable to load non-power-of-two DXT image resolution, falling back to uncompressed\n"); + glDeleteTextures(1, (GLuint *)bindcode); return false; } @@ -1221,14 +1231,18 @@ bool GPU_upload_dxt_texture(ImBuf *ibuf, bool use_srgb) /* set number of mipmap levels we have, needed in case they don't go down to 1x1 */ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, i - 1); + glBindTexture(GL_TEXTURE_2D, 0); return true; #else - UNUSED_VARS(ibuf, use_srgb); + UNUSED_VARS(ibuf, use_srgb, bindcode); return false; #endif } -void GPU_create_gl_tex_compressed(unsigned int *bind, int textarget, Image *ima, ImBuf *ibuf) +static void gpu_create_gl_tex_compressed(unsigned int *bind, + eGPUTextureTarget textarget, + Image *ima, + ImBuf *ibuf) { /* For DDS we only support data, scene linear and sRGB. Converting to * different colorspace would break the compression. */ @@ -1240,19 +1254,19 @@ void GPU_create_gl_tex_compressed(unsigned int *bind, int textarget, Image *ima, #ifndef WITH_DDS (void)ibuf; /* Fall back to uncompressed if DDS isn't enabled */ - GPU_create_gl_tex( + gpu_create_gl_tex( bind, ibuf->rect, NULL, ibuf->x, ibuf->y, textarget, mipmap, half_float, use_srgb, ima); #else - glGenTextures(1, (GLuint *)bind); - glBindTexture(textarget, *bind); - if (textarget == GL_TEXTURE_2D && GPU_upload_dxt_texture(ibuf, use_srgb) == 0) { - glDeleteTextures(1, (GLuint *)bind); - GPU_create_gl_tex( - bind, ibuf->rect, NULL, ibuf->x, ibuf->y, textarget, mipmap, half_float, use_srgb, ima); + if (textarget == TEXTARGET_2D) { + if (GPU_upload_dxt_texture(ibuf, use_srgb, bind)) { + /* All went fine! */ + return; + } } - - glBindTexture(textarget, 0); + /* Fallback. */ + gpu_create_gl_tex( + bind, ibuf->rect, NULL, ibuf->x, ibuf->y, textarget, mipmap, half_float, use_srgb, ima); #endif } @@ -1270,12 +1284,13 @@ void GPU_paint_set_mipmap(Main *bmain, bool mipmap) GTS.texpaint = !mipmap; if (mipmap) { - for (Image *ima = bmain->images.first; ima; ima = ima->id.next) { + + LISTBASE_FOREACH (Image *, ima, &bmain->images) { if (BKE_image_has_opengl_texture(ima)) { if (ima->gpuflag & IMA_GPU_MIPMAP_COMPLETE) { for (int eye = 0; eye < 2; eye++) { for (int a = 0; a < TEXTARGET_COUNT; a++) { - if (ELEM(a, TEXTARGET_TEXTURE_2D, TEXTARGET_TEXTURE_2D_ARRAY)) { + if (ELEM(a, TEXTARGET_2D, TEXTARGET_2D_ARRAY)) { GPUTexture *tex = ima->gputexture[a][eye]; if (tex != NULL) { GPU_texture_bind(tex, 0); @@ -1297,11 +1312,11 @@ void GPU_paint_set_mipmap(Main *bmain, bool mipmap) } } else { - for (Image *ima = bmain->images.first; ima; ima = ima->id.next) { + LISTBASE_FOREACH (Image *, ima, &bmain->images) { if (BKE_image_has_opengl_texture(ima)) { for (int eye = 0; eye < 2; eye++) { for (int a = 0; a < TEXTARGET_COUNT; a++) { - if (ELEM(a, TEXTARGET_TEXTURE_2D, TEXTARGET_TEXTURE_2D_ARRAY)) { + if (ELEM(a, TEXTARGET_2D, TEXTARGET_2D_ARRAY)) { GPUTexture *tex = ima->gputexture[a][eye]; if (tex != NULL) { GPU_texture_bind(tex, 0); @@ -1332,14 +1347,14 @@ void GPU_paint_update_image(Image *ima, ImageUser *iuser, int x, int y, int w, i GPU_free_image(ima); } - GPUTexture *tex = ima->gputexture[TEXTARGET_TEXTURE_2D][0]; + GPUTexture *tex = ima->gputexture[TEXTARGET_2D][0]; /* Check if we need to update the main gputexture. */ if (tex != NULL && tile == ima->tiles.first) { gpu_texture_update_from_ibuf(tex, ima, ibuf, NULL, x, y, w, h); } /* Check if we need to update the array gputexture. */ - tex = ima->gputexture[TEXTARGET_TEXTURE_2D_ARRAY][0]; + tex = ima->gputexture[TEXTARGET_2D_ARRAY][0]; if (tex != NULL) { gpu_texture_update_from_ibuf(tex, ima, ibuf, tile, x, y, w, h); } @@ -1363,12 +1378,10 @@ static void gpu_free_unused_buffers() BLI_mutex_lock(&gpu_texture_queue_mutex); if (gpu_texture_free_queue != NULL) { - for (LinkNode *node = gpu_texture_free_queue; node; node = node->next) { - GPUTexture *tex = node->link; + GPUTexture *tex; + while ((tex = (GPUTexture *)BLI_linklist_pop(&gpu_texture_free_queue))) { GPU_texture_free(tex); } - - BLI_linklist_free(gpu_texture_free_queue, NULL); gpu_texture_free_queue = NULL; } @@ -1412,7 +1425,7 @@ void GPU_free_image(Image *ima) void GPU_free_images(Main *bmain) { if (bmain) { - for (Image *ima = bmain->images.first; ima; ima = ima->id.next) { + LISTBASE_FOREACH (Image *, ima, &bmain->images) { GPU_free_image(ima); } } @@ -1422,7 +1435,7 @@ void GPU_free_images(Main *bmain) void GPU_free_images_anim(Main *bmain) { if (bmain) { - for (Image *ima = bmain->images.first; ima; ima = ima->id.next) { + LISTBASE_FOREACH (Image *, ima, &bmain->images) { if (BKE_image_is_animated(ima)) { GPU_free_image(ima); } @@ -1450,8 +1463,7 @@ void GPU_free_images_old(Main *bmain) lasttime = ctime; - Image *ima = bmain->images.first; - while (ima) { + LISTBASE_FOREACH (Image *, ima, &bmain->images) { if ((ima->flag & IMA_NOCOLLECT) == 0 && ctime - ima->lastused > U.textimeout) { /* If it's in GL memory, deallocate and set time tag to current time * This gives textures a "second chance" to be used before dying. */ @@ -1464,6 +1476,5 @@ void GPU_free_images_old(Main *bmain) BKE_image_free_buffers(ima); } } - ima = ima->id.next; } } diff --git a/source/blender/gpu/intern/gpu_draw_smoke.c b/source/blender/gpu/intern/gpu_texture_smoke.cc index e0b94e20574..0f5aa361fa7 100644 --- a/source/blender/gpu/intern/gpu_draw_smoke.c +++ b/source/blender/gpu/intern/gpu_texture_smoke.cc @@ -62,7 +62,8 @@ static void create_flame_spectrum_texture(float *data) # define MAX_FIRE_ALPHA 0.06f # define FULL_ON_FIRE 100 - float *spec_pixels = MEM_mallocN(TFUNC_WIDTH * 4 * 16 * 16 * sizeof(float), "spec_pixels"); + float *spec_pixels = (float *)MEM_mallocN(TFUNC_WIDTH * 4 * 16 * 16 * sizeof(float), + "spec_pixels"); blackbody_temperature_to_rgb_table(data, TFUNC_WIDTH, 1500, 3000); @@ -105,7 +106,7 @@ static void create_color_ramp(const struct ColorBand *coba, float *data) static GPUTexture *create_transfer_function(int type, const struct ColorBand *coba) { - float *data = MEM_mallocN(sizeof(float) * 4 * TFUNC_WIDTH, __func__); + float *data = (float *)MEM_mallocN(sizeof(float) * 4 * TFUNC_WIDTH, __func__); switch (type) { case TFUNC_FLAME_SPECTRUM: @@ -221,7 +222,7 @@ static GPUTexture *create_color_texture(FluidDomainSettings *fds, int highres) int cell_count = (highres) ? manta_noise_get_cells(fds->fluid) : fds->total_cells; int *dim = (highres) ? fds->res_noise : fds->res; - float *data = MEM_callocN(sizeof(float) * cell_count * 4, "smokeColorTexture"); + float *data = (float *)MEM_callocN(sizeof(float) * cell_count * 4, "smokeColorTexture"); if (data == NULL) { return NULL; diff --git a/source/blender/gpu/intern/gpu_vertex_format_private.h b/source/blender/gpu/intern/gpu_vertex_format_private.h index a850d17a1dd..22373a6394a 100644 --- a/source/blender/gpu/intern/gpu_vertex_format_private.h +++ b/source/blender/gpu/intern/gpu_vertex_format_private.h @@ -26,8 +26,16 @@ #ifndef __GPU_VERTEX_FORMAT_PRIVATE_H__ #define __GPU_VERTEX_FORMAT_PRIVATE_H__ +#ifdef __cplusplus +extern "C" { +#endif + void VertexFormat_pack(GPUVertFormat *format); uint padding(uint offset, uint alignment); uint vertex_buffer_size(const GPUVertFormat *format, uint vertex_len); +#ifdef __cplusplus +} +#endif + #endif /* __GPU_VERTEX_FORMAT_PRIVATE_H__ */ diff --git a/source/blender/gpu/intern/gpu_viewport.c b/source/blender/gpu/intern/gpu_viewport.c index 4d31366f53f..ed5297f0a4a 100644 --- a/source/blender/gpu/intern/gpu_viewport.c +++ b/source/blender/gpu/intern/gpu_viewport.c @@ -38,7 +38,6 @@ #include "DNA_vec_types.h" #include "GPU_framebuffer.h" -#include "GPU_glew.h" #include "GPU_immediate.h" #include "GPU_matrix.h" #include "GPU_texture.h" diff --git a/source/blender/makesdna/DNA_curve_types.h b/source/blender/makesdna/DNA_curve_types.h index b2902407a15..9f724973b6c 100644 --- a/source/blender/makesdna/DNA_curve_types.h +++ b/source/blender/makesdna/DNA_curve_types.h @@ -274,9 +274,12 @@ typedef struct Curve { int selstart, selend; /* text data */ - /** Number of characters (strinfo). */ - int len_wchar; - /** Number of bytes (str - utf8). */ + /** + * Number of characters (unicode code-points) + * This is the length of #Curve.strinfo and the result of `BLI_strlen_utf8(cu->str)`. + */ + int len_char32; + /** Number of bytes: `strlen(Curve.str)`. */ int len; char *str; struct EditFont *editfont; diff --git a/source/blender/makesdna/DNA_image_types.h b/source/blender/makesdna/DNA_image_types.h index 0ffb6c8a76a..20357fc1099 100644 --- a/source/blender/makesdna/DNA_image_types.h +++ b/source/blender/makesdna/DNA_image_types.h @@ -117,14 +117,6 @@ typedef struct ImageTile { #define IMA_NEED_FRAME_RECALC (1 << 3) #define IMA_SHOW_STEREO (1 << 4) -enum { - TEXTARGET_TEXTURE_2D = 0, - TEXTARGET_TEXTURE_CUBE_MAP = 1, - TEXTARGET_TEXTURE_2D_ARRAY = 2, - TEXTARGET_TEXTURE_TILE_MAPPING = 3, - TEXTARGET_COUNT = 4, -}; - typedef struct Image { ID id; diff --git a/source/blender/makesdna/DNA_simulation_types.h b/source/blender/makesdna/DNA_simulation_types.h index 5bb0e50e089..afc59b422c5 100644 --- a/source/blender/makesdna/DNA_simulation_types.h +++ b/source/blender/makesdna/DNA_simulation_types.h @@ -30,16 +30,21 @@ typedef struct Simulation { struct bNodeTree *nodetree; - int flag; + uint32_t flag; + + /** This is the frame in scene time, that the states correspond to. */ float current_frame; + + /** Time since the start of the simulation in simulation time (which might differ from scene + * time). */ float current_simulation_time; char _pad[4]; /** List containing SimulationState objects. */ struct ListBase states; - /** List containing PersistentDataHandleItem objects. */ - struct ListBase persistent_data_handles; + /** List containing SimulationDependency objects. */ + struct ListBase dependencies; } Simulation; typedef struct SimulationState { @@ -54,8 +59,8 @@ typedef struct ParticleSimulationState { SimulationState head; /** Contains the state of the particles at time Simulation->current_frame. */ - int tot_particles; - int next_particle_id; + int32_t tot_particles; + int32_t next_particle_id; struct CustomData attributes; } ParticleSimulationState; @@ -66,20 +71,27 @@ typedef struct ParticleMeshEmitterSimulationState { char _pad[4]; } ParticleMeshEmitterSimulationState; -/** Stores a mapping between an integer handle and a corresponding ID data block. */ -typedef struct PersistentDataHandleItem { - struct PersistentDataHandleItem *next; - struct PersistentDataHandleItem *prev; +/** Stores a reference to data that the simulation depends on. This is partially derived from the + * simulation node tree. */ +typedef struct SimulationDependency { + struct SimulationDependency *next; + struct SimulationDependency *prev; struct ID *id; - int handle; - char _pad[4]; -} PersistentDataHandleItem; + int32_t handle; + uint32_t flag; +} SimulationDependency; /* Simulation.flag */ enum { SIM_DS_EXPAND = (1 << 0), }; +/* SimulationDependency.flag */ +enum { + SIM_DEPENDS_ON_TRANSFORM = (1 << 0), + SIM_DEPENDS_ON_GEOMETRY = (1 << 1), +}; + #define SIM_TYPE_NAME_PARTICLE_SIMULATION "Particle Simulation" #define SIM_TYPE_NAME_PARTICLE_MESH_EMITTER "Particle Mesh Emitter" diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index e6f6ce1e208..d50c0055499 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -733,7 +733,7 @@ typedef struct UserDef { char _pad1[2]; int undomemory; float gpu_viewport_quality DNA_DEPRECATED; - short gp_manhattendist, gp_euclideandist, gp_eraser; + short gp_manhattandist, gp_euclideandist, gp_eraser; /** #eGP_UserdefSettings. */ short gp_settings; char _pad13[4]; diff --git a/source/blender/makesdna/intern/dna_rename_defs.h b/source/blender/makesdna/intern/dna_rename_defs.h index f2cf72843bd..a73fc747f84 100644 --- a/source/blender/makesdna/intern/dna_rename_defs.h +++ b/source/blender/makesdna/intern/dna_rename_defs.h @@ -67,6 +67,7 @@ DNA_STRUCT_RENAME_ELEM(Bone, scaleOut, scale_out_x) DNA_STRUCT_RENAME_ELEM(BrushGpencilSettings, gradient_f, hardeness) DNA_STRUCT_RENAME_ELEM(BrushGpencilSettings, gradient_s, aspect_ratio) DNA_STRUCT_RENAME_ELEM(Camera, YF_dofdist, dof_distance) +DNA_STRUCT_RENAME_ELEM(Curve, len_wchar, len_char32) DNA_STRUCT_RENAME_ELEM(Camera, clipend, clip_end) DNA_STRUCT_RENAME_ELEM(Camera, clipsta, clip_start) DNA_STRUCT_RENAME_ELEM(Collection, dupli_ofs, instance_offset) @@ -90,6 +91,7 @@ DNA_STRUCT_RENAME_ELEM(ParticleSettings, dupliweights, instance_weights) DNA_STRUCT_RENAME_ELEM(Text, name, filepath) DNA_STRUCT_RENAME_ELEM(ThemeSpace, scrubbing_background, time_scrub_background) DNA_STRUCT_RENAME_ELEM(ThemeSpace, show_back_grad, background_type) +DNA_STRUCT_RENAME_ELEM(UserDef, gp_manhattendist, gp_manhattandist) DNA_STRUCT_RENAME_ELEM(VFont, name, filepath) DNA_STRUCT_RENAME_ELEM(View3D, far, clip_end) DNA_STRUCT_RENAME_ELEM(View3D, near, clip_start) diff --git a/source/blender/makesdna/intern/dna_utils.c b/source/blender/makesdna/intern/dna_utils.c index 97f4785374a..3cf5c52a4c6 100644 --- a/source/blender/makesdna/intern/dna_utils.c +++ b/source/blender/makesdna/intern/dna_utils.c @@ -235,7 +235,9 @@ void DNA_alias_maps(enum eDNA_RenameDir version_dir, GHash **r_struct_map, GHash if (version_dir == DNA_RENAME_STATIC_FROM_ALIAS) { const char *renames[][2] = { - {"int8_t", "char"}, /* Note that a char is always unsigned in Blender. */ + /* Disable 'int8_t' until we support 'signed char', since changing negative + * values to a different type isn't supported and will change the value. */ + /* {"int8_t", "char"}, */ {"uint8_t", "uchar"}, {"int16_t", "short"}, {"uint16_t", "ushort"}, diff --git a/source/blender/makesdna/intern/makesdna.c b/source/blender/makesdna/intern/makesdna.c index 182296c0ba2..48258f39604 100644 --- a/source/blender/makesdna/intern/makesdna.c +++ b/source/blender/makesdna/intern/makesdna.c @@ -1530,12 +1530,21 @@ int main(int argc, char **argv) #endif /* if 0 */ -/* even though DNA supports, 'long' shouldn't be used since it can be either 32 or 64bit, - * use int or int64_t instead. +/** + * Disable types: + * + * - 'long': even though DNA supports, 'long' shouldn't be used since it can be either 32 or 64bit, + * use int, int32_t or int64_t instead. + * - 'int8_t': as DNA doesn't yet support 'signed char' types, + * all char types are assumed to be unsigned. + * We should be able to support this, it's just not something which has been added yet. + * * Only valid use would be as a runtime variable if an API expected a long, - * but so far we dont have this happening. */ + * but so far we don't have this happening. + */ #ifdef __GNUC__ # pragma GCC poison long +# pragma GCC poison int8_t #endif #include "DNA_ID.h" diff --git a/source/blender/makesrna/intern/rna_curve.c b/source/blender/makesrna/intern/rna_curve.c index 771235c85aa..8a3186ea7fe 100644 --- a/source/blender/makesrna/intern/rna_curve.c +++ b/source/blender/makesrna/intern/rna_curve.c @@ -570,7 +570,7 @@ static void rna_Curve_body_set(PointerRNA *ptr, const char *value) Curve *cu = (Curve *)ptr->owner_id; - cu->len_wchar = len_chars; + cu->len_char32 = len_chars; cu->len = len_bytes; cu->pos = len_chars; @@ -1206,7 +1206,7 @@ static void rna_def_font(BlenderRNA *UNUSED(brna), StructRNA *srna) RNA_def_property_update(prop, 0, "rna_Curve_update_data"); prop = RNA_def_property(srna, "body_format", PROP_COLLECTION, PROP_NONE); - RNA_def_property_collection_sdna(prop, NULL, "strinfo", "len_wchar"); + RNA_def_property_collection_sdna(prop, NULL, "strinfo", "len_char32"); RNA_def_property_struct_type(prop, "TextCharacterFormat"); RNA_def_property_ui_text(prop, "Character Info", "Stores the style of each character"); diff --git a/source/blender/makesrna/intern/rna_image.c b/source/blender/makesrna/intern/rna_image.c index 887bded8540..e0bcdc504fb 100644 --- a/source/blender/makesrna/intern/rna_image.c +++ b/source/blender/makesrna/intern/rna_image.c @@ -398,7 +398,7 @@ static void rna_Image_resolution_set(PointerRNA *ptr, const float *values) static int rna_Image_bindcode_get(PointerRNA *ptr) { Image *ima = (Image *)ptr->data; - GPUTexture *tex = ima->gputexture[TEXTARGET_TEXTURE_2D][0]; + GPUTexture *tex = ima->gputexture[TEXTARGET_2D][0]; return (tex) ? GPU_texture_opengl_bindcode(tex) : 0; } diff --git a/source/blender/makesrna/intern/rna_image_api.c b/source/blender/makesrna/intern/rna_image_api.c index 41c0e724234..1dcb10ed466 100644 --- a/source/blender/makesrna/intern/rna_image_api.c +++ b/source/blender/makesrna/intern/rna_image_api.c @@ -50,8 +50,6 @@ # include "DNA_image_types.h" # include "DNA_scene_types.h" -# include "GPU_glew.h" - # include "MEM_guardedalloc.h" static void rna_ImagePackedFile_save(ImagePackedFile *imapf, Main *bmain, ReportList *reports) @@ -222,23 +220,24 @@ static int rna_Image_gl_load(Image *image, ReportList *reports, int frame) BKE_imageuser_default(&iuser); iuser.framenr = frame; - GPUTexture *tex = GPU_texture_from_blender(image, &iuser, NULL, GL_TEXTURE_2D); + GPUTexture *tex = GPU_texture_from_blender(image, &iuser, NULL, TEXTARGET_2D); if (tex == NULL) { BKE_reportf(reports, RPT_ERROR, "Failed to load image texture '%s'", image->id.name + 2); - return (int)GL_INVALID_OPERATION; + /* TODO(fclem) this error code makes no sense for vulkan. */ + return 0x0502; /* GL_INVALID_OPERATION */ } - return GL_NO_ERROR; + return 0; /* GL_NO_ERROR */ } static int rna_Image_gl_touch(Image *image, ReportList *reports, int frame) { - int error = GL_NO_ERROR; + int error = 0; /* GL_NO_ERROR */ BKE_image_tag_time(image); - if (image->gputexture[TEXTARGET_TEXTURE_2D] == NULL) { + if (image->gputexture[TEXTARGET_2D] == NULL) { error = rna_Image_gl_load(image, reports, frame); } diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index 6f0192bb810..0359b2ed959 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -38,6 +38,7 @@ #include "BKE_animsys.h" #include "BKE_image.h" #include "BKE_node.h" +#include "BKE_simulation.h" #include "BKE_texture.h" #include "RNA_access.h" @@ -2848,6 +2849,14 @@ static void rna_NodeSocketStandard_value_update(struct bContext *C, PointerRNA * } } +static void rna_NodeSocketStandard_value_and_relation_update(struct bContext *C, PointerRNA *ptr) +{ + rna_NodeSocketStandard_value_update(C, ptr); + bNodeTree *ntree = (bNodeTree *)ptr->owner_id; + Main *bmain = CTX_data_main(C); + ntreeUpdateTree(bmain, ntree); +} + /* ******** Node Types ******** */ static void rna_NodeInternalSocketTemplate_name_get(PointerRNA *ptr, char *value) @@ -8862,7 +8871,8 @@ static void rna_def_node_socket_object(BlenderRNA *brna, RNA_def_property_pointer_sdna(prop, NULL, "value"); RNA_def_property_struct_type(prop, "Object"); RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_update"); + RNA_def_property_update( + prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_and_relation_update"); RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT | PROP_CONTEXT_UPDATE); /* socket interface */ @@ -8896,7 +8906,8 @@ static void rna_def_node_socket_image(BlenderRNA *brna, RNA_def_property_pointer_sdna(prop, NULL, "value"); RNA_def_property_struct_type(prop, "Image"); RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_update"); + RNA_def_property_update( + prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_and_relation_update"); RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT | PROP_CONTEXT_UPDATE); /* socket interface */ diff --git a/source/blender/makesrna/intern/rna_sequencer_api.c b/source/blender/makesrna/intern/rna_sequencer_api.c index 4157747455d..59cedf8fcb8 100644 --- a/source/blender/makesrna/intern/rna_sequencer_api.c +++ b/source/blender/makesrna/intern/rna_sequencer_api.c @@ -370,13 +370,14 @@ static void rna_Sequences_remove( Sequence *seq = seq_ptr->data; Scene *scene = (Scene *)id; - if (BLI_remlink_safe(&ed->seqbase, seq) == false) { + if (BLI_findindex(&ed->seqbase, seq) == -1) { BKE_reportf( reports, RPT_ERROR, "Sequence '%s' not in scene '%s'", seq->name + 2, scene->id.name + 2); return; } - BKE_sequence_free(scene, seq, true); + BKE_sequencer_flag_for_removal(scene, &ed->seqbase, seq); + BKE_sequencer_remove_flagged_sequences(scene, &ed->seqbase); RNA_POINTER_INVALIDATE(seq_ptr); DEG_relations_tag_update(bmain); diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index 494fcec4c31..155f5ab3043 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -1776,87 +1776,28 @@ static const EnumPropertyItem *rna_SpaceProperties_context_itemf(bContext *UNUSE { SpaceProperties *sbuts = (SpaceProperties *)(ptr->data); EnumPropertyItem *item = NULL; - int totitem = 0; - - if (sbuts->pathflag & (1 << BCONTEXT_TOOL)) { - RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_TOOL); - } - - if (totitem) { - RNA_enum_item_add_separator(&item, &totitem); - } - - if (sbuts->pathflag & (1 << BCONTEXT_RENDER)) { - RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_RENDER); - } - - if (sbuts->pathflag & (1 << BCONTEXT_OUTPUT)) { - RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_OUTPUT); - } - - if (sbuts->pathflag & (1 << BCONTEXT_VIEW_LAYER)) { - RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_VIEW_LAYER); - } - - if (sbuts->pathflag & (1 << BCONTEXT_SCENE)) { - RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_SCENE); - } - - if (sbuts->pathflag & (1 << BCONTEXT_WORLD)) { - RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_WORLD); - } - if (totitem) { - RNA_enum_item_add_separator(&item, &totitem); - } - - if (sbuts->pathflag & (1 << BCONTEXT_OBJECT)) { - RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_OBJECT); - } - - if (sbuts->pathflag & (1 << BCONTEXT_MODIFIER)) { - RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_MODIFIER); - } - - if (sbuts->pathflag & (1 << BCONTEXT_SHADERFX)) { - RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_SHADERFX); - } - - if (sbuts->pathflag & (1 << BCONTEXT_PARTICLE)) { - RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_PARTICLE); - } - - if (sbuts->pathflag & (1 << BCONTEXT_PHYSICS)) { - RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_PHYSICS); - } - - if (sbuts->pathflag & (1 << BCONTEXT_CONSTRAINT)) { - RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_CONSTRAINT); - } - - if (sbuts->pathflag & (1 << BCONTEXT_DATA)) { - RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_DATA); - (item + totitem - 1)->icon = sbuts->dataicon; - } - - if (sbuts->pathflag & (1 << BCONTEXT_BONE)) { - RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_BONE); - } - - if (sbuts->pathflag & (1 << BCONTEXT_BONE_CONSTRAINT)) { - RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_BONE_CONSTRAINT); - } - - if (sbuts->pathflag & (1 << BCONTEXT_MATERIAL)) { - RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_MATERIAL); - } + /* We use 32 tabs maximum here so a flag for each can fit into a 32 bit integer flag. + * A theoretical maximum would be BCONTEXT_TOT * 2, with every tab displayed and a spacer + * in every other item. But this size is currently limited by the size of integer + * supported by RNA enums. */ + int context_tabs_array[32]; + int totitem = ED_buttons_tabs_list(sbuts, context_tabs_array); + BLI_assert(totitem <= ARRAY_SIZE(context_tabs_array)); + + int totitem_added = 0; + for (int i = 0; i < totitem; i++) { + if (context_tabs_array[i] == -1) { + RNA_enum_item_add_separator(&item, &totitem_added); + continue; + } - if (totitem) { - RNA_enum_item_add_separator(&item, &totitem); - } + RNA_enum_items_add_value(&item, &totitem_added, buttons_context_items, context_tabs_array[i]); - if (sbuts->pathflag & (1 << BCONTEXT_TEXTURE)) { - RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_TEXTURE); + /* Add the object data icon dynamically for the data tab. */ + if (context_tabs_array[i] == BCONTEXT_DATA) { + (item + totitem_added - 1)->icon = sbuts->dataicon; + } } RNA_enum_item_end(&item, &totitem); diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index 00bbff5cf51..bfc020afb41 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -4988,7 +4988,7 @@ static void rna_def_userdef_edit(BlenderRNA *brna) /* grease pencil */ prop = RNA_def_property(srna, "grease_pencil_manhattan_distance", PROP_INT, PROP_PIXEL); - RNA_def_property_int_sdna(prop, NULL, "gp_manhattendist"); + RNA_def_property_int_sdna(prop, NULL, "gp_manhattandist"); RNA_def_property_range(prop, 0, 100); RNA_def_property_ui_text(prop, "Grease Pencil Manhattan Distance", diff --git a/source/blender/modifiers/intern/MOD_simulation.cc b/source/blender/modifiers/intern/MOD_simulation.cc index d9cc9840e08..85d9b47a861 100644 --- a/source/blender/modifiers/intern/MOD_simulation.cc +++ b/source/blender/modifiers/intern/MOD_simulation.cc @@ -131,6 +131,9 @@ static void panel_draw(const bContext *C, Panel *panel) PointerRNA ob_ptr; modifier_panel_get_property_pointers(C, panel, &ob_ptr, &ptr); + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + uiItemR(layout, &ptr, "simulation", 0, NULL, ICON_NONE); uiItemR(layout, &ptr, "data_path", 0, NULL, ICON_NONE); diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 80720f5206a..c53e01ac80f 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -160,7 +160,7 @@ set(SRC shader/nodes/node_shader_bsdf_velvet.c shader/nodes/node_shader_bump.c shader/nodes/node_shader_camera.c - shader/nodes/node_shader_clamp.c + shader/nodes/node_shader_clamp.cc shader/nodes/node_shader_common.c shader/nodes/node_shader_curves.c shader/nodes/node_shader_displacement.c @@ -278,6 +278,7 @@ set(SRC intern/node_common.c intern/node_exec.c intern/node_socket.cc + intern/node_tree_dependencies.cc intern/node_tree_multi_function.cc intern/node_tree_ref.cc intern/node_util.c @@ -292,6 +293,7 @@ set(SRC NOD_composite.h NOD_derived_node_tree.hh NOD_function.h + NOD_node_tree_dependencies.hh NOD_node_tree_multi_function.hh NOD_node_tree_ref.hh NOD_shader.h diff --git a/source/blender/nodes/NOD_derived_node_tree.hh b/source/blender/nodes/NOD_derived_node_tree.hh index d79bd9031b8..b39c9fd1a23 100644 --- a/source/blender/nodes/NOD_derived_node_tree.hh +++ b/source/blender/nodes/NOD_derived_node_tree.hh @@ -181,7 +181,7 @@ class DerivedNodeTree : NonCopyable, NonMovable { Vector<DInputSocket *> input_sockets_; Vector<DOutputSocket *> output_sockets_; - Map<const bNodeType *, Vector<DNode *>> nodes_by_type_; + MultiValueMap<const bNodeType *, DNode *> nodes_by_type_; public: DerivedNodeTree(bNodeTree *btree, NodeTreeRefMap &node_tree_refs); @@ -483,13 +483,7 @@ inline Span<const DNode *> DerivedNodeTree::nodes_by_type(StringRefNull idname) inline Span<const DNode *> DerivedNodeTree::nodes_by_type(const bNodeType *nodetype) const { - const Vector<DNode *> *nodes = nodes_by_type_.lookup_ptr(nodetype); - if (nodes == nullptr) { - return {}; - } - else { - return *nodes; - } + return nodes_by_type_.lookup(nodetype); } inline Span<const DSocket *> DerivedNodeTree::sockets() const diff --git a/source/blender/nodes/NOD_node_tree_dependencies.hh b/source/blender/nodes/NOD_node_tree_dependencies.hh new file mode 100644 index 00000000000..ca7059caa5f --- /dev/null +++ b/source/blender/nodes/NOD_node_tree_dependencies.hh @@ -0,0 +1,79 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __NOD_NODE_TREE_DEPENDENCIES_H__ +#define __NOD_NODE_TREE_DEPENDENCIES_H__ + +#include "BLI_vector_set.hh" + +#include "DNA_ID.h" +#include "DNA_object_types.h" + +struct bNodeTree; + +namespace blender::nodes { + +class NodeTreeDependencies { + private: + VectorSet<Object *> transform_deps_; + VectorSet<Object *> geometry_deps_; + VectorSet<ID *> id_deps_; + + public: + void add_transform_dependency(Object *object) + { + if (object == nullptr) { + return; + } + transform_deps_.add(object); + id_deps_.add(&object->id); + } + + void add_geometry_dependency(Object *object) + { + if (object == nullptr) { + return; + } + geometry_deps_.add(object); + id_deps_.add(&object->id); + } + + bool depends_on(ID *id) const + { + return id_deps_.contains(id); + } + + Span<Object *> transform_dependencies() + { + return transform_deps_; + } + + Span<Object *> geometry_dependencies() + { + return geometry_deps_; + } + + Span<ID *> id_dependencies() + { + return id_deps_; + } +}; + +NodeTreeDependencies find_node_tree_dependencies(bNodeTree &ntree); + +} // namespace blender::nodes + +#endif /* __NOD_NODE_TREE_DEPENDENCIES_H__ */ diff --git a/source/blender/nodes/NOD_node_tree_ref.hh b/source/blender/nodes/NOD_node_tree_ref.hh index ebf5709ef50..13656c2e940 100644 --- a/source/blender/nodes/NOD_node_tree_ref.hh +++ b/source/blender/nodes/NOD_node_tree_ref.hh @@ -47,6 +47,7 @@ #include "BLI_array.hh" #include "BLI_linear_allocator.hh" #include "BLI_map.hh" +#include "BLI_multi_value_map.hh" #include "BLI_string_ref.hh" #include "BLI_timeit.hh" #include "BLI_utility_mixins.hh" @@ -162,7 +163,7 @@ class NodeTreeRef : NonCopyable, NonMovable { Vector<SocketRef *> sockets_by_id_; Vector<InputSocketRef *> input_sockets_; Vector<OutputSocketRef *> output_sockets_; - Map<const bNodeType *, Vector<NodeRef *>> nodes_by_type_; + MultiValueMap<const bNodeType *, NodeRef *> nodes_by_type_; public: NodeTreeRef(bNodeTree *btree); @@ -411,13 +412,7 @@ inline Span<const NodeRef *> NodeTreeRef::nodes_by_type(StringRefNull idname) co inline Span<const NodeRef *> NodeTreeRef::nodes_by_type(const bNodeType *nodetype) const { - const Vector<NodeRef *> *nodes = nodes_by_type_.lookup_ptr(nodetype); - if (nodes == nullptr) { - return {}; - } - else { - return *nodes; - } + return nodes_by_type_.lookup(nodetype); } inline Span<const SocketRef *> NodeTreeRef::sockets() const diff --git a/source/blender/nodes/intern/derived_node_tree.cc b/source/blender/nodes/intern/derived_node_tree.cc index b7c78cb1499..5414ea9208a 100644 --- a/source/blender/nodes/intern/derived_node_tree.cc +++ b/source/blender/nodes/intern/derived_node_tree.cc @@ -321,7 +321,7 @@ BLI_NOINLINE void DerivedNodeTree::store_in_this_and_init_ids( node->id_ = node_index; const bNodeType *nodetype = node->node_ref_->bnode()->typeinfo; - nodes_by_type_.lookup_or_add_default(nodetype).append(node); + nodes_by_type_.add(nodetype, node); for (DInputSocket *socket : node->inputs_) { socket->id_ = sockets_by_id_.append_and_get_index(socket); diff --git a/source/blender/nodes/intern/node_socket.cc b/source/blender/nodes/intern/node_socket.cc index 49868505d68..57c8d51975e 100644 --- a/source/blender/nodes/intern/node_socket.cc +++ b/source/blender/nodes/intern/node_socket.cc @@ -586,10 +586,10 @@ static bNodeSocketType *make_socket_type_string() class ObjectSocketMultiFunction : public blender::fn::MultiFunction { private: - const Object *object_; + Object *object_; public: - ObjectSocketMultiFunction(const Object *object) : object_(object) + ObjectSocketMultiFunction(Object *object) : object_(object) { blender::fn::MFSignatureBuilder signature = this->get_builder("Object Socket"); signature.depends_on_context(); @@ -631,7 +631,7 @@ static bNodeSocketType *make_socket_type_object() return blender::fn::MFDataType::ForSingle<blender::bke::PersistentObjectHandle>(); }; socktype->expand_in_mf_network = [](blender::nodes::SocketMFNetworkBuilder &builder) { - const Object *object = builder.socket_default_value<bNodeSocketValueObject>()->value; + Object *object = builder.socket_default_value<bNodeSocketValueObject>()->value; builder.construct_generator_fn<ObjectSocketMultiFunction>(object); }; return socktype; diff --git a/source/blender/nodes/intern/node_tree_dependencies.cc b/source/blender/nodes/intern/node_tree_dependencies.cc new file mode 100644 index 00000000000..efe75a10f7e --- /dev/null +++ b/source/blender/nodes/intern/node_tree_dependencies.cc @@ -0,0 +1,57 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "NOD_node_tree_dependencies.hh" + +#include "DNA_node_types.h" + +#include "BKE_node.h" + +namespace blender::nodes { + +static void add_dependencies_of_node_tree(bNodeTree &ntree, NodeTreeDependencies &r_dependencies) +{ + /* TODO: Do a bit more sophisticated parsing to see which dependencies are really required. */ + LISTBASE_FOREACH (bNode *, node, &ntree.nodes) { + LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) { + if (socket->type == SOCK_OBJECT) { + Object *object = ((bNodeSocketValueObject *)socket->default_value)->value; + if (object != nullptr) { + r_dependencies.add_transform_dependency(object); + if (object->type == OB_MESH) { + r_dependencies.add_geometry_dependency(object); + } + } + } + } + + if (node->type == NODE_GROUP) { + bNodeTree *group = (bNodeTree *)node->id; + if (group != nullptr) { + add_dependencies_of_node_tree(*group, r_dependencies); + } + } + } +} + +NodeTreeDependencies find_node_tree_dependencies(bNodeTree &ntree) +{ + NodeTreeDependencies dependencies; + add_dependencies_of_node_tree(ntree, dependencies); + return dependencies; +} + +} // namespace blender::nodes diff --git a/source/blender/nodes/intern/node_tree_ref.cc b/source/blender/nodes/intern/node_tree_ref.cc index 186ca750f10..47669bc5ca2 100644 --- a/source/blender/nodes/intern/node_tree_ref.cc +++ b/source/blender/nodes/intern/node_tree_ref.cc @@ -79,7 +79,7 @@ NodeTreeRef::NodeTreeRef(bNodeTree *btree) : btree_(btree) for (NodeRef *node : nodes_by_id_) { const bNodeType *nodetype = node->bnode_->typeinfo; - nodes_by_type_.lookup_or_add_default(nodetype).append(node); + nodes_by_type_.add(nodetype, node); } } diff --git a/source/blender/nodes/shader/nodes/node_shader_clamp.c b/source/blender/nodes/shader/nodes/node_shader_clamp.cc index 808f9686f0a..1077f616a62 100644 --- a/source/blender/nodes/shader/nodes/node_shader_clamp.c +++ b/source/blender/nodes/shader/nodes/node_shader_clamp.cc @@ -50,6 +50,30 @@ static int gpu_shader_clamp(GPUMaterial *mat, GPU_stack_link(mat, node, "clamp_range", in, out); } +static void sh_node_clamp_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) +{ + static blender::fn::CustomMF_SI_SI_SI_SO<float, float, float, float> minmax_fn{ + "Clamp (Min Max)", + [](float value, float min, float max) { return std::min(std::max(value, min), max); }}; + static blender::fn::CustomMF_SI_SI_SI_SO<float, float, float, float> range_fn{ + "Clamp (Range)", [](float value, float a, float b) { + if (a < b) { + return clamp_f(value, a, b); + } + else { + return clamp_f(value, b, a); + } + }}; + + int clamp_type = builder.bnode().custom1; + if (clamp_type == NODE_CLAMP_MINMAX) { + builder.set_matching_fn(minmax_fn); + } + else { + builder.set_matching_fn(range_fn); + } +} + void register_node_type_sh_clamp(void) { static bNodeType ntype; @@ -58,6 +82,7 @@ void register_node_type_sh_clamp(void) node_type_socket_templates(&ntype, sh_node_clamp_in, sh_node_clamp_out); node_type_init(&ntype, node_shader_init_clamp); node_type_gpu(&ntype, gpu_shader_clamp); + ntype.expand_in_mf_network = sh_node_clamp_expand_in_mf_network; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/simulation/nodes/node_sim_particle_mesh_emitter.cc b/source/blender/nodes/simulation/nodes/node_sim_particle_mesh_emitter.cc index 2de7be2d3eb..859ad81656b 100644 --- a/source/blender/nodes/simulation/nodes/node_sim_particle_mesh_emitter.cc +++ b/source/blender/nodes/simulation/nodes/node_sim_particle_mesh_emitter.cc @@ -20,7 +20,7 @@ static bNodeSocketTemplate sim_node_particle_mesh_emitter_in[] = { {SOCK_OBJECT, N_("Object")}, - {SOCK_FLOAT, N_("Rate"), 10.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX}, + {SOCK_FLOAT, N_("Rate"), 100.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX}, {-1, ""}, }; diff --git a/source/blender/nodes/simulation/nodes/node_sim_set_particle_attribute.cc b/source/blender/nodes/simulation/nodes/node_sim_set_particle_attribute.cc index 8696dbe340c..8f5c6818cb4 100644 --- a/source/blender/nodes/simulation/nodes/node_sim_set_particle_attribute.cc +++ b/source/blender/nodes/simulation/nodes/node_sim_set_particle_attribute.cc @@ -18,6 +18,7 @@ #include "node_simulation_util.h" static bNodeSocketTemplate sim_node_set_particle_attribute_in[] = { + {SOCK_CONTROL_FLOW, N_("Execute")}, {SOCK_STRING, N_("Name")}, {SOCK_FLOAT, N_("Float"), 0.0f, 0.0f, 0.0f, 0.0f, -10000.0f, 10000.0f}, {SOCK_INT, N_("Int"), 0, 0, 0, 0, -10000, 10000}, @@ -38,7 +39,7 @@ static void sim_node_set_particle_attribute_update(bNodeTree *UNUSED(ntree), bNo { int index = 0; LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { - if (index >= 1) { + if (index >= 2) { nodeSetSocketAvailability(sock, sock->type == node->custom1); } index++; diff --git a/source/blender/python/BPY_extern.h b/source/blender/python/BPY_extern.h index 5c6e0b0a308..049b919b4a4 100644 --- a/source/blender/python/BPY_extern.h +++ b/source/blender/python/BPY_extern.h @@ -82,23 +82,23 @@ bool BPY_execute_text(struct bContext *C, bool BPY_execute_string_as_number(struct bContext *C, const char *imports[], const char *expr, - const bool verbose, + const char *report_prefix, double *r_value); bool BPY_execute_string_as_intptr(struct bContext *C, const char *imports[], const char *expr, - const bool verbose, + const char *report_prefix, intptr_t *r_value); bool BPY_execute_string_as_string_and_size(struct bContext *C, const char *imports[], const char *expr, - const bool verbose, + const char *report_prefix, char **r_value, size_t *r_value_size); bool BPY_execute_string_as_string(struct bContext *C, const char *imports[], const char *expr, - const bool verbose, + const char *report_prefix, char **r_value); bool BPY_execute_string_ex(struct bContext *C, diff --git a/source/blender/python/generic/py_capi_utils.c b/source/blender/python/generic/py_capi_utils.c index 9c84a4bb824..406dbdafe22 100644 --- a/source/blender/python/generic/py_capi_utils.c +++ b/source/blender/python/generic/py_capi_utils.c @@ -634,7 +634,7 @@ error_cleanup: PyObject *PyC_ExceptionBuffer_Simple(void) { - PyObject *string_io_buf; + PyObject *string_io_buf = NULL; PyObject *error_type, *error_value, *error_traceback; @@ -648,7 +648,19 @@ PyObject *PyC_ExceptionBuffer_Simple(void) return NULL; } - string_io_buf = PyObject_Str(error_value); + if (PyErr_GivenExceptionMatches(error_type, PyExc_SyntaxError)) { + /* Special exception for syntax errors, + * in these cases the full error is verbose and not very useful, + * just use the initial text so we know what the error is. */ + if (PyTuple_CheckExact(error_value) && PyTuple_GET_SIZE(error_value) >= 1) { + string_io_buf = PyObject_Str(PyTuple_GET_ITEM(error_value, 0)); + } + } + + if (string_io_buf == NULL) { + string_io_buf = PyObject_Str(error_value); + } + /* Python does this too */ if (UNLIKELY(string_io_buf == NULL)) { string_io_buf = PyUnicode_FromFormat("<unprintable %s object>", Py_TYPE(error_value)->tp_name); @@ -804,8 +816,12 @@ void PyC_MainModule_Restore(PyObject *main_mod) Py_XDECREF(main_mod); } -/* Must be called before Py_Initialize, - * expects output of BKE_appdir_folder_id(BLENDER_PYTHON, NULL). */ +/** + * - Must be called before #Py_Initialize. + * - Expects output of `BKE_appdir_folder_id(BLENDER_PYTHON, NULL)`. + * - Note that the `PYTHONPATH` environment variable isn't reliable, see T31506. + Use #Py_SetPythonHome instead. + */ void PyC_SetHomePath(const char *py_path_bundle) { if (py_path_bundle == NULL) { @@ -824,24 +840,14 @@ void PyC_SetHomePath(const char *py_path_bundle) /* OSX allow file/directory names to contain : character (represented as / in the Finder) * but current Python lib (release 3.1.1) doesn't handle these correctly */ if (strchr(py_path_bundle, ':')) { - printf( - "Warning : Blender application is located in a path containing : or / chars\ - \nThis may make python import function fail\n"); - } -# endif - -# if 0 /* disable for now [#31506] - campbell */ -# ifdef _WIN32 - /* cmake/MSVC debug build crashes without this, why only - * in this case is unknown.. */ - { - /*BLI_setenv("PYTHONPATH", py_path_bundle)*/; + fprintf(stderr, + "Warning! Blender application is located in a path containing ':' or '/' chars\n" + "This may make python import function fail\n"); } -# endif # endif { - static wchar_t py_path_bundle_wchar[1024]; + wchar_t py_path_bundle_wchar[1024]; /* Can't use this, on linux gives bug: #23018, * TODO: try LANG="en_US.UTF-8" /usr/bin/blender, suggested 2008 */ diff --git a/source/blender/python/intern/bpy_capi_utils.c b/source/blender/python/intern/bpy_capi_utils.c index 89ef2f40a30..27eea80f1f6 100644 --- a/source/blender/python/intern/bpy_capi_utils.c +++ b/source/blender/python/intern/bpy_capi_utils.c @@ -97,7 +97,10 @@ void BPy_reports_write_stdout(const ReportList *reports, const char *header) } } -bool BPy_errors_to_report_ex(ReportList *reports, const bool use_full, const bool use_location) +bool BPy_errors_to_report_ex(ReportList *reports, + const char *error_prefix, + const bool use_full, + const bool use_location) { PyObject *pystring; @@ -124,40 +127,38 @@ bool BPy_errors_to_report_ex(ReportList *reports, const bool use_full, const boo return 0; } + if (error_prefix == NULL) { + /* Not very helpful, better than nothing. */ + error_prefix = "Python"; + } + if (use_location) { const char *filename; int lineno; - PyObject *pystring_format; /* workaround, see below */ - const char *cstring; - PyC_FileAndNum(&filename, &lineno); if (filename == NULL) { filename = "<unknown location>"; } -#if 0 /* ARG!. workaround for a bug in blenders use of vsnprintf */ BKE_reportf(reports, RPT_ERROR, - "%s\nlocation: %s:%d\n", + TIP_("%s: %s\nlocation: %s:%d\n"), + error_prefix, _PyUnicode_AsString(pystring), filename, lineno); -#else - pystring_format = PyUnicode_FromFormat( - TIP_("%s\nlocation: %s:%d\n"), _PyUnicode_AsString(pystring), filename, lineno); - - cstring = _PyUnicode_AsString(pystring_format); - BKE_report(reports, RPT_ERROR, cstring); - - /* not exactly needed. just for testing */ - fprintf(stderr, TIP_("%s\nlocation: %s:%d\n"), cstring, filename, lineno); - Py_DECREF(pystring_format); /* workaround */ -#endif + /* Not exactly needed. Useful for developers tracking down issues. */ + fprintf(stderr, + TIP_("%s: %s\nlocation: %s:%d\n"), + error_prefix, + _PyUnicode_AsString(pystring), + filename, + lineno); } else { - BKE_report(reports, RPT_ERROR, _PyUnicode_AsString(pystring)); + BKE_reportf(reports, RPT_ERROR, "%s: %s", error_prefix, _PyUnicode_AsString(pystring)); } Py_DECREF(pystring); @@ -166,5 +167,5 @@ bool BPy_errors_to_report_ex(ReportList *reports, const bool use_full, const boo bool BPy_errors_to_report(ReportList *reports) { - return BPy_errors_to_report_ex(reports, true, true); + return BPy_errors_to_report_ex(reports, NULL, true, true); } diff --git a/source/blender/python/intern/bpy_capi_utils.h b/source/blender/python/intern/bpy_capi_utils.h index 97344ce1326..2d654336f8f 100644 --- a/source/blender/python/intern/bpy_capi_utils.h +++ b/source/blender/python/intern/bpy_capi_utils.h @@ -39,8 +39,10 @@ char *BPy_enum_as_string(const struct EnumPropertyItem *item); short BPy_reports_to_error(struct ReportList *reports, PyObject *exception, const bool clear); void BPy_reports_write_stdout(const struct ReportList *reports, const char *header); bool BPy_errors_to_report_ex(struct ReportList *reports, + const char *error_prefix, const bool use_full, const bool use_location); +bool BPy_errors_to_report_brief_with_prefix(struct ReportList *reports, const char *error_prefix); bool BPy_errors_to_report(struct ReportList *reports); /* TODO - find a better solution! */ diff --git a/source/blender/python/intern/bpy_driver.c b/source/blender/python/intern/bpy_driver.c index 5e2162c9e2d..d48ad2b197c 100644 --- a/source/blender/python/intern/bpy_driver.c +++ b/source/blender/python/intern/bpy_driver.c @@ -411,8 +411,10 @@ static PyObject *bpy_pydriver_depsgraph_as_pyobject(struct Depsgraph *depsgraph) return pyrna_struct_CreatePyObject(&depsgraph_ptr); } -/* Adds a variable 'depsgraph' to the driver variables. This can then be used to obtain evaluated - * datablocks, and the current view layer and scene. See T75553. */ +/** + * Adds a variable 'depsgraph' to the driver variables. This can then be used to obtain evaluated + * data-blocks, and the current view layer and scene. See T75553. + */ static void bpy_pydriver_namespace_add_depsgraph(PyObject *driver_vars, struct Depsgraph *depsgraph) { @@ -428,17 +430,18 @@ static void bpy_pydriver_namespace_add_depsgraph(PyObject *driver_vars, } } -/* This evals py driver expressions, 'expr' is a Python expression that - * should evaluate to a float number, which is returned. +/** + * This evaluates Python driver expressions, `driver_orig->expression` + * is a Python expression that should evaluate to a float number, which is returned. * * (old)note: PyGILState_Ensure() isn't always called because python can call * the bake operator which intern starts a thread which calls scene update - * which does a driver update. to avoid a deadlock check PyC_IsInterpreterActive() - * if PyGILState_Ensure() is needed - see [#27683] + * which does a driver update. to avoid a deadlock check #PyC_IsInterpreterActive() + * if #PyGILState_Ensure() is needed, see T27683. * - * (new)note: checking if python is running is not threadsafe [#28114] + * (new)note: checking if python is running is not thread-safe T28114 * now release the GIL on python operator execution instead, using - * PyEval_SaveThread() / PyEval_RestoreThread() so we don't lock up blender. + * #PyEval_SaveThread() / #PyEval_RestoreThread() so we don't lock up blender. * * For copy-on-write we always cache expressions and write errors in the * original driver, otherwise these would get freed while editing. Due to diff --git a/source/blender/python/intern/bpy_interface.c b/source/blender/python/intern/bpy_interface.c index a880d2cd285..f91afa414ce 100644 --- a/source/blender/python/intern/bpy_interface.c +++ b/source/blender/python/intern/bpy_interface.c @@ -258,11 +258,13 @@ void BPY_python_start(int argc, const char **argv) PyThreadState *py_tstate = NULL; const char *py_path_bundle = BKE_appdir_folder_id(BLENDER_SYSTEM_PYTHON, NULL); - /* not essential but nice to set our name */ - static wchar_t program_path_wchar[FILE_MAX]; /* python holds a reference */ - BLI_strncpy_wchar_from_utf8( - program_path_wchar, BKE_appdir_program_path(), ARRAY_SIZE(program_path_wchar)); - Py_SetProgramName(program_path_wchar); + /* Not essential but nice to set our name. */ + { + const char *program_path = BKE_appdir_program_path(); + wchar_t program_path_wchar[FILE_MAX]; + BLI_strncpy_wchar_from_utf8(program_path_wchar, program_path, ARRAY_SIZE(program_path_wchar)); + Py_SetProgramName(program_path_wchar); + } /* must run before python initializes */ PyImport_ExtendInittab(bpy_internal_modules); @@ -615,8 +617,11 @@ void BPY_DECREF_RNA_INVALIDATE(void *pyob_ptr) /** * \return success */ -bool BPY_execute_string_as_number( - bContext *C, const char *imports[], const char *expr, const bool verbose, double *r_value) +bool BPY_execute_string_as_number(bContext *C, + const char *imports[], + const char *expr, + const char *report_prefix, + double *r_value) { PyGILState_STATE gilstate; bool ok = true; @@ -635,8 +640,8 @@ bool BPY_execute_string_as_number( ok = PyC_RunString_AsNumber(imports, expr, "<expr as number>", r_value); if (ok == false) { - if (verbose) { - BPy_errors_to_report_ex(CTX_wm_reports(C), false, false); + if (report_prefix != NULL) { + BPy_errors_to_report_ex(CTX_wm_reports(C), report_prefix, false, false); } else { PyErr_Clear(); @@ -654,7 +659,7 @@ bool BPY_execute_string_as_number( bool BPY_execute_string_as_string_and_size(bContext *C, const char *imports[], const char *expr, - const bool verbose, + const char *report_prefix, char **r_value, size_t *r_value_size) { @@ -672,8 +677,8 @@ bool BPY_execute_string_as_string_and_size(bContext *C, ok = PyC_RunString_AsStringAndSize(imports, expr, "<expr as str>", r_value, r_value_size); if (ok == false) { - if (verbose) { - BPy_errors_to_report_ex(CTX_wm_reports(C), false, false); + if (report_prefix != NULL) { + BPy_errors_to_report_ex(CTX_wm_reports(C), false, false, report_prefix); } else { PyErr_Clear(); @@ -685,12 +690,15 @@ bool BPY_execute_string_as_string_and_size(bContext *C, return ok; } -bool BPY_execute_string_as_string( - bContext *C, const char *imports[], const char *expr, const bool verbose, char **r_value) +bool BPY_execute_string_as_string(bContext *C, + const char *imports[], + const char *expr, + const char *report_prefix, + char **r_value) { size_t value_dummy_size; return BPY_execute_string_as_string_and_size( - C, imports, expr, verbose, r_value, &value_dummy_size); + C, imports, expr, report_prefix, r_value, &value_dummy_size); } /** @@ -698,8 +706,11 @@ bool BPY_execute_string_as_string( * * \return success */ -bool BPY_execute_string_as_intptr( - bContext *C, const char *imports[], const char *expr, const bool verbose, intptr_t *r_value) +bool BPY_execute_string_as_intptr(bContext *C, + const char *imports[], + const char *expr, + const char *report_prefix, + intptr_t *r_value) { BLI_assert(r_value && expr); PyGILState_STATE gilstate; @@ -715,8 +726,8 @@ bool BPY_execute_string_as_intptr( ok = PyC_RunString_AsIntPtr(imports, expr, "<expr as intptr>", r_value); if (ok == false) { - if (verbose) { - BPy_errors_to_report_ex(CTX_wm_reports(C), false, false); + if (report_prefix != NULL) { + BPy_errors_to_report_ex(CTX_wm_reports(C), report_prefix, false, false); } else { PyErr_Clear(); diff --git a/source/blender/simulation/CMakeLists.txt b/source/blender/simulation/CMakeLists.txt index 243b056db74..cbc6ee65303 100644 --- a/source/blender/simulation/CMakeLists.txt +++ b/source/blender/simulation/CMakeLists.txt @@ -43,7 +43,9 @@ set(SRC intern/implicit_eigen.cpp intern/particle_allocator.cc intern/particle_function.cc + intern/particle_mesh_emitter.cc intern/simulation_collect_influences.cc + intern/simulation_solver_influences.cc intern/simulation_solver.cc intern/simulation_update.cc @@ -52,7 +54,9 @@ set(SRC intern/implicit.h intern/particle_allocator.hh intern/particle_function.hh + intern/particle_mesh_emitter.hh intern/simulation_collect_influences.hh + intern/simulation_solver_influences.hh intern/simulation_solver.hh intern/time_interval.hh diff --git a/source/blender/simulation/SIM_simulation_update.hh b/source/blender/simulation/SIM_simulation_update.hh index 40b62bfb58a..efdfde8a4de 100644 --- a/source/blender/simulation/SIM_simulation_update.hh +++ b/source/blender/simulation/SIM_simulation_update.hh @@ -27,6 +27,8 @@ void update_simulation_in_depsgraph(Depsgraph *depsgraph, Scene *scene_cow, Simulation *simulation_cow); -} +bool update_simulation_dependencies(Simulation *simulation); + +} // namespace blender::sim #endif /* __SIM_SIMULATION_UPDATE_HH__ */ diff --git a/source/blender/simulation/intern/particle_allocator.cc b/source/blender/simulation/intern/particle_allocator.cc index eb1e998e63a..e47a6354d81 100644 --- a/source/blender/simulation/intern/particle_allocator.cc +++ b/source/blender/simulation/intern/particle_allocator.cc @@ -16,6 +16,8 @@ #include "particle_allocator.hh" +#include "BLI_rand.hh" + namespace blender::sim { AttributesAllocator::~AttributesAllocator() @@ -67,8 +69,15 @@ fn::MutableAttributesRef ParticleAllocator::allocate(int size) ids[pindex] = start_id + pindex; } } + else if (name == "Hash") { + MutableSpan<int> hashes = attributes.get<int>("Hash"); + RandomNumberGenerator rng(hash_seed_ ^ (uint32_t)next_id_); + for (int pindex : IndexRange(size)) { + hashes[pindex] = (int)rng.get_uint32(); + } + } else { - type.fill_uninitialized(info.default_of(i), attributes.get(i).buffer(), size); + type.fill_uninitialized(info.default_of(i), attributes.get(i).data(), size); } } return attributes; diff --git a/source/blender/simulation/intern/particle_allocator.hh b/source/blender/simulation/intern/particle_allocator.hh index 1e7578a75ed..1c412508fe6 100644 --- a/source/blender/simulation/intern/particle_allocator.hh +++ b/source/blender/simulation/intern/particle_allocator.hh @@ -70,10 +70,11 @@ class ParticleAllocator : NonCopyable, NonMovable { private: AttributesAllocator attributes_allocator_; std::atomic<int> next_id_; + uint32_t hash_seed_; public: - ParticleAllocator(const fn::AttributesInfo &attributes_info, int next_id) - : attributes_allocator_(attributes_info), next_id_(next_id) + ParticleAllocator(const fn::AttributesInfo &attributes_info, int next_id, uint32_t hash_seed) + : attributes_allocator_(attributes_info), next_id_(next_id), hash_seed_(hash_seed) { } diff --git a/source/blender/simulation/intern/particle_function.cc b/source/blender/simulation/intern/particle_function.cc index 935ef7983d9..035e6d50e21 100644 --- a/source/blender/simulation/intern/particle_function.cc +++ b/source/blender/simulation/intern/particle_function.cc @@ -49,19 +49,17 @@ ParticleFunction::ParticleFunction(const fn::MultiFunction *global_fn, } } -ParticleFunctionEvaluator::ParticleFunctionEvaluator( - const ParticleFunction &particle_fn, - const SimulationSolveContext &solve_context, - const ParticleChunkContext &particle_chunk_context) +ParticleFunctionEvaluator::ParticleFunctionEvaluator(const ParticleFunction &particle_fn, + const SimulationSolveContext &solve_context, + const ParticleChunkContext &particles) : particle_fn_(particle_fn), solve_context_(solve_context), - particle_chunk_context_(particle_chunk_context), - mask_(particle_chunk_context_.index_mask()), + particles_(particles), + mask_(particles_.index_mask), outputs_(particle_fn_.output_types_.size(), nullptr) { - global_context_.add_global_context("PersistentDataHandleMap", &solve_context_.handle_map()); - per_particle_context_.add_global_context("PersistentDataHandleMap", - &solve_context_.handle_map()); + global_context_.add_global_context("PersistentDataHandleMap", &solve_context_.handle_map); + per_particle_context_.add_global_context("PersistentDataHandleMap", &solve_context_.handle_map); } ParticleFunctionEvaluator::~ParticleFunctionEvaluator() @@ -92,8 +90,10 @@ void ParticleFunctionEvaluator::compute() fn::GVSpan ParticleFunctionEvaluator::get(int output_index, StringRef expected_name) const { #ifdef DEBUG - StringRef real_name = particle_fn_.output_names_[output_index]; - BLI_assert(expected_name == real_name); + if (expected_name != "") { + StringRef real_name = particle_fn_.output_names_[output_index]; + BLI_assert(expected_name == real_name); + } BLI_assert(is_computed_); #endif UNUSED_VARS_NDEBUG(expected_name); @@ -116,8 +116,9 @@ void ParticleFunctionEvaluator::compute_globals() fn::MFParamsBuilder params(*particle_fn_.global_fn_, mask_.min_array_size()); /* Add input parameters. */ + ParticleFunctionInputContext input_context{solve_context_, particles_}; for (const ParticleFunctionInput *input : particle_fn_.global_inputs_) { - input->add_input(particle_chunk_context_.attributes(), params, resources_); + input->add_input(input_context, params, resources_); } /* Add output parameters. */ @@ -143,8 +144,9 @@ void ParticleFunctionEvaluator::compute_per_particle() fn::MFParamsBuilder params(*particle_fn_.per_particle_fn_, mask_.min_array_size()); /* Add input parameters. */ + ParticleFunctionInputContext input_context{solve_context_, particles_}; for (const ParticleFunctionInput *input : particle_fn_.per_particle_inputs_) { - input->add_input(particle_chunk_context_.attributes(), params, resources_); + input->add_input(input_context, params, resources_); } /* Add output parameters. */ diff --git a/source/blender/simulation/intern/particle_function.hh b/source/blender/simulation/intern/particle_function.hh index eec4a700383..c9b35640b47 100644 --- a/source/blender/simulation/intern/particle_function.hh +++ b/source/blender/simulation/intern/particle_function.hh @@ -26,10 +26,15 @@ namespace blender::sim { +struct ParticleFunctionInputContext { + const SimulationSolveContext &solve_context; + const ParticleChunkContext &particles; +}; + class ParticleFunctionInput { public: virtual ~ParticleFunctionInput() = default; - virtual void add_input(fn::AttributesRef attributes, + virtual void add_input(ParticleFunctionInputContext &context, fn::MFParamsBuilder ¶ms, ResourceCollector &resources) const = 0; }; @@ -61,7 +66,7 @@ class ParticleFunctionEvaluator { ResourceCollector resources_; const ParticleFunction &particle_fn_; const SimulationSolveContext &solve_context_; - const ParticleChunkContext &particle_chunk_context_; + const ParticleChunkContext &particles_; IndexMask mask_; fn::MFContextBuilder global_context_; fn::MFContextBuilder per_particle_context_; @@ -71,13 +76,13 @@ class ParticleFunctionEvaluator { public: ParticleFunctionEvaluator(const ParticleFunction &particle_fn, const SimulationSolveContext &solve_context, - const ParticleChunkContext &particle_chunk_context); + const ParticleChunkContext &particles); ~ParticleFunctionEvaluator(); void compute(); - fn::GVSpan get(int output_index, StringRef expected_name) const; + fn::GVSpan get(int output_index, StringRef expected_name = "") const; - template<typename T> fn::VSpan<T> get(int output_index, StringRef expected_name) const + template<typename T> fn::VSpan<T> get(int output_index, StringRef expected_name = "") const { return this->get(output_index, expected_name).typed<T>(); } diff --git a/source/blender/simulation/intern/particle_mesh_emitter.cc b/source/blender/simulation/intern/particle_mesh_emitter.cc new file mode 100644 index 00000000000..c1482a29cb7 --- /dev/null +++ b/source/blender/simulation/intern/particle_mesh_emitter.cc @@ -0,0 +1,352 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "particle_mesh_emitter.hh" + +#include "BLI_float4x4.hh" +#include "BLI_rand.hh" +#include "BLI_vector_adaptor.hh" + +#include "BKE_mesh_runtime.h" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" + +namespace blender::sim { + +ParticleMeshEmitter::~ParticleMeshEmitter() = default; + +struct EmitterSettings { + Object *object; + float rate; +}; + +static BLI_NOINLINE void compute_birth_times(float rate, + TimeInterval emit_interval, + ParticleMeshEmitterSimulationState &state, + Vector<float> &r_birth_times) +{ + const float time_between_particles = 1.0f / rate; + int counter = 0; + while (true) { + counter++; + const float time_offset = counter * time_between_particles; + const float birth_time = state.last_birth_time + time_offset; + if (birth_time > emit_interval.stop()) { + break; + } + if (birth_time <= emit_interval.start()) { + continue; + } + r_birth_times.append(birth_time); + } +} + +static BLI_NOINLINE Span<MLoopTri> get_mesh_triangles(Mesh &mesh) +{ + const MLoopTri *triangles = BKE_mesh_runtime_looptri_ensure(&mesh); + int amount = BKE_mesh_runtime_looptri_len(&mesh); + return Span(triangles, amount); +} + +static BLI_NOINLINE void compute_triangle_areas(Mesh &mesh, + Span<MLoopTri> triangles, + MutableSpan<float> r_areas) +{ + assert_same_size(triangles, r_areas); + + for (int i : triangles.index_range()) { + const MLoopTri &tri = triangles[i]; + + const float3 v1 = mesh.mvert[mesh.mloop[tri.tri[0]].v].co; + const float3 v2 = mesh.mvert[mesh.mloop[tri.tri[1]].v].co; + const float3 v3 = mesh.mvert[mesh.mloop[tri.tri[2]].v].co; + + const float area = area_tri_v3(v1, v2, v3); + r_areas[i] = area; + } +} + +static BLI_NOINLINE void compute_triangle_weights(Mesh &mesh, + Span<MLoopTri> triangles, + MutableSpan<float> r_weights) +{ + assert_same_size(triangles, r_weights); + compute_triangle_areas(mesh, triangles, r_weights); +} + +static BLI_NOINLINE void compute_cumulative_distribution(Span<float> weights, + MutableSpan<float> r_cumulative_weights) +{ + BLI_assert(weights.size() + 1 == r_cumulative_weights.size()); + + r_cumulative_weights[0] = 0; + for (int i : weights.index_range()) { + r_cumulative_weights[i + 1] = r_cumulative_weights[i] + weights[i]; + } +} + +static void sample_cumulative_distribution_recursive(RandomNumberGenerator &rng, + int amount, + int start, + int one_after_end, + Span<float> cumulative_weights, + VectorAdaptor<int> &r_sampled_indices) +{ + BLI_assert(start <= one_after_end); + const int size = one_after_end - start; + if (size == 0) { + BLI_assert(amount == 0); + } + else if (amount == 0) { + return; + } + else if (size == 1) { + r_sampled_indices.append_n_times(start, amount); + } + else { + const int middle = start + size / 2; + const float left_weight = cumulative_weights[middle] - cumulative_weights[start]; + const float right_weight = cumulative_weights[one_after_end] - cumulative_weights[middle]; + BLI_assert(left_weight >= 0.0f && right_weight >= 0.0f); + const float weight_sum = left_weight + right_weight; + BLI_assert(weight_sum > 0.0f); + + const float left_factor = left_weight / weight_sum; + const float right_factor = right_weight / weight_sum; + + int left_amount = amount * left_factor; + int right_amount = amount * right_factor; + + if (left_amount + right_amount < amount) { + BLI_assert(left_amount + right_amount + 1 == amount); + const float weight_per_item = weight_sum / amount; + const float total_remaining_weight = weight_sum - + (left_amount + right_amount) * weight_per_item; + const float left_remaining_weight = left_weight - left_amount * weight_per_item; + const float left_remaining_factor = left_remaining_weight / total_remaining_weight; + if (rng.get_float() < left_remaining_factor) { + left_amount++; + } + else { + right_amount++; + } + } + + sample_cumulative_distribution_recursive( + rng, left_amount, start, middle, cumulative_weights, r_sampled_indices); + sample_cumulative_distribution_recursive( + rng, right_amount, middle, one_after_end, cumulative_weights, r_sampled_indices); + } +} + +static BLI_NOINLINE void sample_cumulative_distribution(RandomNumberGenerator &rng, + Span<float> cumulative_weights, + MutableSpan<int> r_samples) +{ + VectorAdaptor<int> sampled_indices(r_samples); + sample_cumulative_distribution_recursive(rng, + r_samples.size(), + 0, + cumulative_weights.size() - 1, + cumulative_weights, + sampled_indices); + BLI_assert(sampled_indices.is_full()); +} + +static BLI_NOINLINE bool sample_weighted_buckets(RandomNumberGenerator &rng, + Span<float> weights, + MutableSpan<int> r_samples) +{ + Array<float> cumulative_weights(weights.size() + 1); + compute_cumulative_distribution(weights, cumulative_weights); + + if (r_samples.size() > 0 && cumulative_weights.as_span().last() == 0.0f) { + /* All weights are zero. */ + return false; + } + + sample_cumulative_distribution(rng, cumulative_weights, r_samples); + return true; +} + +static BLI_NOINLINE void sample_looptris(RandomNumberGenerator &rng, + Mesh &mesh, + Span<MLoopTri> triangles, + Span<int> triangles_to_sample, + MutableSpan<float3> r_sampled_positions, + MutableSpan<float3> r_sampled_normals) +{ + assert_same_size(triangles_to_sample, r_sampled_positions, r_sampled_normals); + + MLoop *loops = mesh.mloop; + MVert *verts = mesh.mvert; + + for (uint i : triangles_to_sample.index_range()) { + const uint triangle_index = triangles_to_sample[i]; + const MLoopTri &triangle = triangles[triangle_index]; + + const float3 v1 = verts[loops[triangle.tri[0]].v].co; + const float3 v2 = verts[loops[triangle.tri[1]].v].co; + const float3 v3 = verts[loops[triangle.tri[2]].v].co; + + const float3 bary_coords = rng.get_barycentric_coordinates(); + + float3 position; + interp_v3_v3v3v3(position, v1, v2, v3, bary_coords); + + float3 normal; + normal_tri_v3(normal, v1, v2, v3); + + r_sampled_positions[i] = position; + r_sampled_normals[i] = normal; + } +} + +static BLI_NOINLINE bool compute_new_particle_attributes(ParticleEmitterContext &context, + EmitterSettings &settings, + ParticleMeshEmitterSimulationState &state, + Vector<float3> &r_positions, + Vector<float3> &r_velocities, + Vector<float> &r_birth_times) +{ + if (settings.object == nullptr) { + return false; + } + if (settings.rate <= 0.000001f) { + return false; + } + if (settings.object->type != OB_MESH) { + return false; + } + Mesh &mesh = *(Mesh *)settings.object->data; + if (mesh.totvert == 0) { + return false; + } + + const float start_time = context.emit_interval.start(); + const uint32_t seed = DefaultHash<StringRef>{}(state.head.name); + RandomNumberGenerator rng{(*(uint32_t *)&start_time) ^ seed}; + + compute_birth_times(settings.rate, context.emit_interval, state, r_birth_times); + const int particle_amount = r_birth_times.size(); + if (particle_amount == 0) { + return false; + } + + const float last_birth_time = r_birth_times.last(); + rng.shuffle(r_birth_times.as_mutable_span()); + + Span<MLoopTri> triangles = get_mesh_triangles(mesh); + if (triangles.is_empty()) { + return false; + } + + Array<float> triangle_weights(triangles.size()); + compute_triangle_weights(mesh, triangles, triangle_weights); + + Array<int> triangles_to_sample(particle_amount); + if (!sample_weighted_buckets(rng, triangle_weights, triangles_to_sample)) { + return false; + } + + r_positions.resize(particle_amount); + r_velocities.resize(particle_amount); + sample_looptris(rng, mesh, triangles, triangles_to_sample, r_positions, r_velocities); + + if (context.solve_context.dependency_animations.is_object_transform_changing(*settings.object)) { + Array<float4x4> local_to_world_matrices(particle_amount); + context.solve_context.dependency_animations.get_object_transforms( + *settings.object, r_birth_times, local_to_world_matrices); + + for (int i : IndexRange(particle_amount)) { + const float4x4 &position_to_world = local_to_world_matrices[i]; + const float4x4 normal_to_world = position_to_world.inverted_transposed_affine(); + r_positions[i] = position_to_world * r_positions[i]; + r_velocities[i] = normal_to_world * r_velocities[i]; + } + } + else { + const float4x4 position_to_world = settings.object->obmat; + const float4x4 normal_to_world = position_to_world.inverted_transposed_affine(); + for (int i : IndexRange(particle_amount)) { + r_positions[i] = position_to_world * r_positions[i]; + r_velocities[i] = normal_to_world * r_velocities[i]; + } + } + + for (int i : IndexRange(particle_amount)) { + r_velocities[i].normalize(); + } + + state.last_birth_time = last_birth_time; + return true; +} + +static BLI_NOINLINE EmitterSettings compute_settings(const fn::MultiFunction &inputs_fn, + ParticleEmitterContext &context) +{ + EmitterSettings parameters; + + fn::MFContextBuilder mf_context; + mf_context.add_global_context("PersistentDataHandleMap", &context.solve_context.handle_map); + + fn::MFParamsBuilder mf_params{inputs_fn, 1}; + bke::PersistentObjectHandle object_handle; + mf_params.add_uninitialized_single_output(&object_handle, "Object"); + mf_params.add_uninitialized_single_output(¶meters.rate, "Rate"); + + inputs_fn.call(IndexRange(1), mf_params, mf_context); + + parameters.object = context.solve_context.handle_map.lookup(object_handle); + return parameters; +} + +void ParticleMeshEmitter::emit(ParticleEmitterContext &context) const +{ + auto *state = context.lookup_state<ParticleMeshEmitterSimulationState>(own_state_name_); + if (state == nullptr) { + return; + } + + EmitterSettings settings = compute_settings(inputs_fn_, context); + + Vector<float3> new_positions; + Vector<float3> new_velocities; + Vector<float> new_birth_times; + + if (!compute_new_particle_attributes( + context, settings, *state, new_positions, new_velocities, new_birth_times)) { + return; + } + + for (StringRef name : particle_names_) { + ParticleAllocator *allocator = context.try_get_particle_allocator(name); + if (allocator == nullptr) { + continue; + } + + int amount = new_positions.size(); + fn::MutableAttributesRef attributes = allocator->allocate(amount); + + attributes.get<float3>("Position").copy_from(new_positions); + attributes.get<float3>("Velocity").copy_from(new_velocities); + attributes.get<float>("Birth Time").copy_from(new_birth_times); + } +} + +} // namespace blender::sim diff --git a/source/blender/simulation/intern/particle_mesh_emitter.hh b/source/blender/simulation/intern/particle_mesh_emitter.hh new file mode 100644 index 00000000000..601697c9986 --- /dev/null +++ b/source/blender/simulation/intern/particle_mesh_emitter.hh @@ -0,0 +1,49 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __SIM_PARTICLE_MESH_EMITTER_HH__ +#define __SIM_PARTICLE_MESH_EMITTER_HH__ + +#include "simulation_solver_influences.hh" + +#include "FN_multi_function.hh" + +namespace blender::sim { + +class ParticleMeshEmitter final : public ParticleEmitter { + private: + std::string own_state_name_; + Array<std::string> particle_names_; + const fn::MultiFunction &inputs_fn_; + + public: + ParticleMeshEmitter(std::string own_state_name, + Array<std::string> particle_names, + const fn::MultiFunction &inputs_fn) + : own_state_name_(std::move(own_state_name)), + particle_names_(particle_names), + inputs_fn_(inputs_fn) + { + } + + ~ParticleMeshEmitter(); + + void emit(ParticleEmitterContext &context) const override; +}; + +} // namespace blender::sim + +#endif /* __SIM_PARTICLE_MESH_EMITTER_HH__ */ diff --git a/source/blender/simulation/intern/simulation_collect_influences.cc b/source/blender/simulation/intern/simulation_collect_influences.cc index 764e587d157..4d339f9e323 100644 --- a/source/blender/simulation/intern/simulation_collect_influences.cc +++ b/source/blender/simulation/intern/simulation_collect_influences.cc @@ -16,6 +16,7 @@ #include "simulation_collect_influences.hh" #include "particle_function.hh" +#include "particle_mesh_emitter.hh" #include "FN_attributes_ref.hh" #include "FN_multi_function_network_evaluation.hh" @@ -23,38 +24,125 @@ #include "NOD_node_tree_multi_function.hh" +#include "DEG_depsgraph_query.h" + +#include "BLI_hash.h" #include "BLI_rand.hh" +#include "BLI_set.hh" namespace blender::sim { +using fn::GVSpan; +using fn::MFContextBuilder; +using fn::MFDataType; +using fn::MFDummyNode; +using fn::MFFunctionNode; +using fn::MFInputSocket; +using fn::MFNetwork; +using fn::MFNetworkEvaluator; +using fn::MFNode; +using fn::MFOutputSocket; +using fn::MFParamsBuilder; +using fn::MFParamType; +using fn::MultiFunction; +using fn::VSpan; +using nodes::DerivedNodeTree; +using nodes::DInputSocket; +using nodes::DNode; +using nodes::DOutputSocket; +using nodes::DParentNode; +using nodes::MFNetworkTreeMap; +using nodes::NodeTreeRefMap; + struct DummyDataSources { - Map<const fn::MFOutputSocket *, std::string> particle_attributes; + Map<const MFOutputSocket *, std::string> particle_attributes; + Set<const MFOutputSocket *> simulation_time; + Set<const MFOutputSocket *> scene_time; }; extern "C" { void WM_clipboard_text_set(const char *buf, bool selection); } -static std::string dnode_to_path(const nodes::DNode &dnode) +static std::string dnode_to_path(const DNode &dnode) { std::string path; - for (const nodes::DParentNode *parent = dnode.parent(); parent; parent = parent->parent()) { + for (const DParentNode *parent = dnode.parent(); parent; parent = parent->parent()) { path = parent->node_ref().name() + "/" + path; } path = path + dnode.name(); return path; } -static Span<const nodes::DNode *> get_particle_simulation_nodes(const nodes::DerivedNodeTree &tree) +struct CollectContext : NonCopyable, NonMovable { + SimulationInfluences &influences; + RequiredStates &required_states; + ResourceCollector &resources; + MFNetworkTreeMap &network_map; + MFNetwork &network; + const DerivedNodeTree &tree; + + DummyDataSources data_sources; + Span<const DNode *> particle_simulation_nodes; + Map<const DNode *, std::string> node_paths; + + CollectContext(SimulationInfluences &influences, + RequiredStates &required_states, + ResourceCollector &resources, + MFNetworkTreeMap &network_map) + : influences(influences), + required_states(required_states), + resources(resources), + network_map(network_map), + network(network_map.network()), + tree(network_map.tree()) + { + particle_simulation_nodes = tree.nodes_by_type("SimulationNodeParticleSimulation"); + } +}; + +static const ParticleAction *create_particle_action(CollectContext &context, + const DOutputSocket &dsocket, + Span<StringRefNull> particle_names); + +static const ParticleAction *create_particle_action(CollectContext &context, + const DInputSocket &dsocket, + Span<StringRefNull> particle_names) +{ + BLI_assert(dsocket.bsocket()->type == SOCK_CONTROL_FLOW); + if (dsocket.linked_sockets().size() != 1) { + return nullptr; + } + return create_particle_action(context, *dsocket.linked_sockets()[0], particle_names); +} + +static StringRefNull get_identifier(CollectContext &context, const DNode &dnode) +{ + return context.node_paths.lookup_or_add_cb(&dnode, [&]() { return dnode_to_path(dnode); }); +} + +static Span<const DNode *> nodes_by_type(CollectContext &context, StringRefNull idname) +{ + return context.tree.nodes_by_type(idname); +} + +static Array<StringRefNull> find_linked_particle_simulations(CollectContext &context, + const DOutputSocket &output_socket) { - return tree.nodes_by_type("SimulationNodeParticleSimulation"); + VectorSet<StringRefNull> names; + for (const DInputSocket *target_socket : output_socket.linked_sockets()) { + if (target_socket->node().idname() == "SimulationNodeParticleSimulation") { + names.add(get_identifier(context, target_socket->node())); + } + } + return names.as_span(); } /* Returns true on success. */ -static bool compute_global_inputs(nodes::MFNetworkTreeMap &network_map, +static bool compute_global_inputs(MFNetworkTreeMap &network_map, ResourceCollector &resources, - Span<const fn::MFInputSocket *> sockets, - MutableSpan<fn::GMutableSpan> r_results) + Span<const MFInputSocket *> sockets, + MutableSpan<GMutableSpan> r_results) { int amount = sockets.size(); if (amount == 0) { @@ -65,28 +153,28 @@ static bool compute_global_inputs(nodes::MFNetworkTreeMap &network_map, return false; } - fn::MFNetworkEvaluator network_fn{{}, sockets}; - fn::MFParamsBuilder params{network_fn, 1}; + MFNetworkEvaluator network_fn{{}, sockets}; + MFParamsBuilder params{network_fn, 1}; for (int param_index : network_fn.param_indices()) { - fn::MFParamType param_type = network_fn.param_type(param_index); - BLI_assert(param_type.category() == fn::MFParamType::Category::SingleOutput); /* For now. */ - const fn::CPPType &type = param_type.data_type().single_type(); + MFParamType param_type = network_fn.param_type(param_index); + BLI_assert(param_type.category() == MFParamType::Category::SingleOutput); /* For now. */ + const CPPType &type = param_type.data_type().single_type(); void *buffer = resources.linear_allocator().allocate(type.size(), type.alignment()); resources.add(buffer, type.destruct_cb(), AT); - fn::GMutableSpan span{type, buffer, 1}; + GMutableSpan span{type, buffer, 1}; r_results[param_index] = span; params.add_uninitialized_single_output(span); } - fn::MFContextBuilder context; + MFContextBuilder context; network_fn.call(IndexRange(1), params, context); return true; } static std::optional<Array<std::string>> compute_global_string_inputs( - nodes::MFNetworkTreeMap &network_map, Span<const fn::MFInputSocket *> sockets) + MFNetworkTreeMap &network_map, Span<const MFInputSocket *> sockets) { ResourceCollector local_resources; - Array<fn::GMutableSpan> computed_values(sockets.size(), NoInitialization()); + Array<GMutableSpan> computed_values(sockets.size(), NoInitialization()); if (!compute_global_inputs(network_map, local_resources, sockets, computed_values)) { return {}; } @@ -98,107 +186,177 @@ static std::optional<Array<std::string>> compute_global_string_inputs( return strings; } -static void find_and_deduplicate_particle_attribute_nodes(nodes::MFNetworkTreeMap &network_map, - DummyDataSources &r_data_sources) +/** + * This will find all the particle attribute input nodes. Then it will compute the attribute names + * by evaluating the network (those names should not depend on per particle data). In the end, + * input nodes that access the same attribute are combined. + */ +static void prepare_particle_attribute_nodes(CollectContext &context) { - fn::MFNetwork &network = network_map.network(); - const nodes::DerivedNodeTree &tree = network_map.tree(); - - Span<const nodes::DNode *> attribute_dnodes = tree.nodes_by_type( - "SimulationNodeParticleAttribute"); + Span<const DNode *> attribute_dnodes = nodes_by_type(context, "SimulationNodeParticleAttribute"); - Vector<fn::MFInputSocket *> name_sockets; - for (const nodes::DNode *dnode : attribute_dnodes) { - fn::MFInputSocket &name_socket = network_map.lookup_dummy(dnode->input(0)); + Vector<MFInputSocket *> name_sockets; + for (const DNode *dnode : attribute_dnodes) { + MFInputSocket &name_socket = context.network_map.lookup_dummy(dnode->input(0)); name_sockets.append(&name_socket); } - std::optional<Array<std::string>> attribute_names = compute_global_string_inputs(network_map, - name_sockets); + std::optional<Array<std::string>> attribute_names = compute_global_string_inputs( + context.network_map, name_sockets); if (!attribute_names.has_value()) { return; } - Map<std::pair<std::string, fn::MFDataType>, Vector<fn::MFNode *>> - attribute_nodes_by_name_and_type; + MultiValueMap<std::pair<std::string, MFDataType>, MFNode *> attribute_nodes_by_name_and_type; for (int i : attribute_names->index_range()) { - attribute_nodes_by_name_and_type - .lookup_or_add_default( - {(*attribute_names)[i], name_sockets[i]->node().output(0).data_type()}) - .append(&name_sockets[i]->node()); + attribute_nodes_by_name_and_type.add( + {(*attribute_names)[i], name_sockets[i]->node().output(0).data_type()}, + &name_sockets[i]->node()); } - Map<const fn::MFOutputSocket *, std::string> attribute_inputs; + Map<const MFOutputSocket *, std::string> attribute_inputs; for (auto item : attribute_nodes_by_name_and_type.items()) { StringRef attribute_name = item.key.first; - fn::MFDataType data_type = item.key.second; - Span<fn::MFNode *> nodes = item.value; + MFDataType data_type = item.key.second; + Span<MFNode *> nodes = item.value; - fn::MFOutputSocket &new_attribute_socket = network.add_input( + MFOutputSocket &new_attribute_socket = context.network.add_input( "Attribute '" + attribute_name + "'", data_type); - for (fn::MFNode *node : nodes) { - network.relink(node->output(0), new_attribute_socket); + for (MFNode *node : nodes) { + context.network.relink(node->output(0), new_attribute_socket); + } + context.network.remove(nodes); + + context.data_sources.particle_attributes.add_new(&new_attribute_socket, attribute_name); + } +} + +static void prepare_time_input_nodes(CollectContext &context) +{ + Span<const DNode *> time_input_dnodes = nodes_by_type(context, "SimulationNodeTime"); + Vector<const DNode *> simulation_time_inputs; + Vector<const DNode *> scene_time_inputs; + for (const DNode *dnode : time_input_dnodes) { + NodeSimInputTimeType type = (NodeSimInputTimeType)dnode->node_ref().bnode()->custom1; + switch (type) { + case NODE_SIM_INPUT_SIMULATION_TIME: { + simulation_time_inputs.append(dnode); + break; + } + case NODE_SIM_INPUT_SCENE_TIME: { + scene_time_inputs.append(dnode); + break; + } } - network.remove(nodes); + } - r_data_sources.particle_attributes.add_new(&new_attribute_socket, attribute_name); + if (simulation_time_inputs.size() > 0) { + MFOutputSocket &new_socket = context.network.add_input("Simulation Time", + MFDataType::ForSingle<float>()); + for (const DNode *dnode : simulation_time_inputs) { + MFOutputSocket &old_socket = context.network_map.lookup_dummy(dnode->output(0)); + context.network.relink(old_socket, new_socket); + context.network.remove(old_socket.node()); + } + context.data_sources.simulation_time.add(&new_socket); + } + if (scene_time_inputs.size() > 0) { + MFOutputSocket &new_socket = context.network.add_input("Scene Time", + MFDataType::ForSingle<float>()); + for (const DNode *dnode : scene_time_inputs) { + MFOutputSocket &old_socket = context.network_map.lookup_dummy(dnode->output(0)); + context.network.relink(old_socket, new_socket); + context.network.remove(old_socket.node()); + } + context.data_sources.scene_time.add(&new_socket); } } class ParticleAttributeInput : public ParticleFunctionInput { private: std::string attribute_name_; - const fn::CPPType &attribute_type_; + const CPPType &attribute_type_; public: - ParticleAttributeInput(std::string attribute_name, const fn::CPPType &attribute_type) + ParticleAttributeInput(std::string attribute_name, const CPPType &attribute_type) : attribute_name_(std::move(attribute_name)), attribute_type_(attribute_type) { } - void add_input(fn::AttributesRef attributes, - fn::MFParamsBuilder ¶ms, + void add_input(ParticleFunctionInputContext &context, + MFParamsBuilder ¶ms, ResourceCollector &UNUSED(resources)) const override { - std::optional<fn::GSpan> span = attributes.try_get(attribute_name_, attribute_type_); + std::optional<GSpan> span = context.particles.attributes.try_get(attribute_name_, + attribute_type_); if (span.has_value()) { params.add_readonly_single_input(*span); } else { - params.add_readonly_single_input(fn::GVSpan::FromDefault(attribute_type_)); + params.add_readonly_single_input(GVSpan::FromDefault(attribute_type_)); } } }; +class SceneTimeInput : public ParticleFunctionInput { + void add_input(ParticleFunctionInputContext &context, + MFParamsBuilder ¶ms, + ResourceCollector &resources) const override + { + const float time = DEG_get_ctime(&context.solve_context.depsgraph); + float *time_ptr = &resources.construct<float>(AT, time); + params.add_readonly_single_input(time_ptr); + } +}; + +class SimulationTimeInput : public ParticleFunctionInput { + void add_input(ParticleFunctionInputContext &context, + MFParamsBuilder ¶ms, + ResourceCollector &resources) const override + { + /* TODO: Vary this per particle. */ + const float time = context.solve_context.solve_interval.stop(); + float *time_ptr = &resources.construct<float>(AT, time); + params.add_readonly_single_input(time_ptr); + } +}; + static const ParticleFunction *create_particle_function_for_inputs( - Span<const fn::MFInputSocket *> sockets_to_compute, - ResourceCollector &resources, - DummyDataSources &data_sources) + CollectContext &context, Span<const MFInputSocket *> sockets_to_compute) { BLI_assert(sockets_to_compute.size() >= 1); - const fn::MFNetwork &network = sockets_to_compute[0]->node().network(); + const MFNetwork &network = sockets_to_compute[0]->node().network(); - VectorSet<const fn::MFOutputSocket *> dummy_deps; - VectorSet<const fn::MFInputSocket *> unlinked_input_deps; + VectorSet<const MFOutputSocket *> dummy_deps; + VectorSet<const MFInputSocket *> unlinked_input_deps; network.find_dependencies(sockets_to_compute, dummy_deps, unlinked_input_deps); BLI_assert(unlinked_input_deps.size() == 0); Vector<const ParticleFunctionInput *> per_particle_inputs; - for (const fn::MFOutputSocket *socket : dummy_deps) { - const std::string *attribute_name = data_sources.particle_attributes.lookup_ptr(socket); - if (attribute_name == nullptr) { - return nullptr; + for (const MFOutputSocket *socket : dummy_deps) { + if (context.data_sources.particle_attributes.contains(socket)) { + const std::string *attribute_name = context.data_sources.particle_attributes.lookup_ptr( + socket); + if (attribute_name == nullptr) { + return nullptr; + } + per_particle_inputs.append(&context.resources.construct<ParticleAttributeInput>( + AT, *attribute_name, socket->data_type().single_type())); + } + else if (context.data_sources.scene_time.contains(socket)) { + per_particle_inputs.append(&context.resources.construct<SceneTimeInput>(AT)); + } + else if (context.data_sources.simulation_time.contains(socket)) { + per_particle_inputs.append(&context.resources.construct<SimulationTimeInput>(AT)); } - per_particle_inputs.append(&resources.construct<ParticleAttributeInput>( - AT, *attribute_name, socket->data_type().single_type())); } - const fn::MultiFunction &per_particle_fn = resources.construct<fn::MFNetworkEvaluator>( + const MultiFunction &per_particle_fn = context.resources.construct<MFNetworkEvaluator>( AT, dummy_deps.as_span(), sockets_to_compute); Array<bool> output_is_global(sockets_to_compute.size(), false); - const ParticleFunction &particle_fn = resources.construct<ParticleFunction>( + const ParticleFunction &particle_fn = context.resources.construct<ParticleFunction>( AT, nullptr, &per_particle_fn, @@ -220,13 +378,12 @@ class ParticleFunctionForce : public ParticleForce { void add_force(ParticleForceContext &context) const override { - IndexMask mask = context.particle_chunk().index_mask(); - MutableSpan<float3> r_combined_force = context.force_dst(); + IndexMask mask = context.particles.index_mask; + MutableSpan<float3> r_combined_force = context.force_dst; - ParticleFunctionEvaluator evaluator{ - particle_fn_, context.solve_context(), context.particle_chunk()}; + ParticleFunctionEvaluator evaluator{particle_fn_, context.solve_context, context.particles}; evaluator.compute(); - fn::VSpan<float3> forces = evaluator.get<float3>(0, "Force"); + VSpan<float3> forces = evaluator.get<float3>(0, "Force"); for (int64_t i : mask) { r_combined_force[i] += forces[i]; @@ -234,221 +391,365 @@ class ParticleFunctionForce : public ParticleForce { } }; -static Vector<const ParticleForce *> create_forces_for_particle_simulation( - const nodes::DNode &simulation_node, - nodes::MFNetworkTreeMap &network_map, - ResourceCollector &resources, - DummyDataSources &data_sources) +static void create_forces_for_particle_simulation(CollectContext &context, + const DNode &simulation_node) { Vector<const ParticleForce *> forces; - for (const nodes::DOutputSocket *origin_socket : - simulation_node.input(2, "Forces").linked_sockets()) { - const nodes::DNode &origin_node = origin_socket->node(); + for (const DOutputSocket *origin_socket : simulation_node.input(2, "Forces").linked_sockets()) { + const DNode &origin_node = origin_socket->node(); if (origin_node.idname() != "SimulationNodeForce") { continue; } - const fn::MFInputSocket &force_socket = network_map.lookup_dummy( + const MFInputSocket &force_socket = context.network_map.lookup_dummy( origin_node.input(0, "Force")); - const ParticleFunction *particle_fn = create_particle_function_for_inputs( - {&force_socket}, resources, data_sources); + const ParticleFunction *particle_fn = create_particle_function_for_inputs(context, + {&force_socket}); if (particle_fn == nullptr) { continue; } - const ParticleForce &force = resources.construct<ParticleFunctionForce>(AT, *particle_fn); + const ParticleForce &force = context.resources.construct<ParticleFunctionForce>(AT, + *particle_fn); forces.append(&force); } - return forces; + + StringRef particle_name = get_identifier(context, simulation_node); + context.influences.particle_forces.add_multiple_as(particle_name, forces); +} + +static void collect_forces(CollectContext &context) +{ + for (const DNode *dnode : context.particle_simulation_nodes) { + create_forces_for_particle_simulation(context, *dnode); + } +} + +static ParticleEmitter *create_particle_emitter(CollectContext &context, const DNode &dnode) +{ + Array<StringRefNull> names = find_linked_particle_simulations(context, dnode.output(0)); + if (names.size() == 0) { + return nullptr; + } + + Array<const MFInputSocket *> input_sockets{dnode.inputs().size()}; + for (int i : input_sockets.index_range()) { + input_sockets[i] = &context.network_map.lookup_dummy(dnode.input(i)); + } + + if (context.network.have_dummy_or_unlinked_dependencies(input_sockets)) { + return nullptr; + } + + MultiFunction &inputs_fn = context.resources.construct<MFNetworkEvaluator>( + AT, Span<const MFOutputSocket *>(), input_sockets.as_span()); + + StringRefNull own_state_name = get_identifier(context, dnode); + context.required_states.add(own_state_name, SIM_TYPE_NAME_PARTICLE_MESH_EMITTER); + ParticleEmitter &emitter = context.resources.construct<ParticleMeshEmitter>( + AT, own_state_name, names.as_span(), inputs_fn); + return &emitter; } -static void collect_forces(nodes::MFNetworkTreeMap &network_map, - ResourceCollector &resources, - DummyDataSources &data_sources, - SimulationInfluences &r_influences) +static void collect_emitters(CollectContext &context) { - for (const nodes::DNode *dnode : get_particle_simulation_nodes(network_map.tree())) { - std::string name = dnode_to_path(*dnode); - Vector<const ParticleForce *> forces = create_forces_for_particle_simulation( - *dnode, network_map, resources, data_sources); - r_influences.particle_forces.add_new(std::move(name), std::move(forces)); + for (const DNode *dnode : nodes_by_type(context, "SimulationNodeParticleMeshEmitter")) { + ParticleEmitter *emitter = create_particle_emitter(context, *dnode); + if (emitter != nullptr) { + context.influences.particle_emitters.append(emitter); + } } } -class MyBasicEmitter : public ParticleEmitter { +static void collect_birth_events(CollectContext &context) +{ + for (const DNode *event_dnode : nodes_by_type(context, "SimulationNodeParticleBirthEvent")) { + const DInputSocket &execute_input = event_dnode->input(0); + if (execute_input.linked_sockets().size() != 1) { + continue; + } + + Array<StringRefNull> particle_names = find_linked_particle_simulations(context, + event_dnode->output(0)); + + const DOutputSocket &execute_source = *execute_input.linked_sockets()[0]; + const ParticleAction *action = create_particle_action(context, execute_source, particle_names); + if (action == nullptr) { + continue; + } + + for (StringRefNull particle_name : particle_names) { + context.influences.particle_birth_actions.add_as(particle_name, action); + } + } +} + +static void collect_time_step_events(CollectContext &context) +{ + for (const DNode *event_dnode : nodes_by_type(context, "SimulationNodeParticleTimeStepEvent")) { + const DInputSocket &execute_input = event_dnode->input(0); + if (execute_input.linked_sockets().size() != 1) { + continue; + } + + Array<StringRefNull> particle_names = find_linked_particle_simulations(context, + event_dnode->output(0)); + + const DOutputSocket &execute_source = *execute_input.linked_sockets()[0]; + const ParticleAction *action = create_particle_action(context, execute_source, particle_names); + if (action == nullptr) { + continue; + } + + NodeSimParticleTimeStepEventType type = + (NodeSimParticleTimeStepEventType)event_dnode->node_ref().bnode()->custom1; + if (type == NODE_PARTICLE_TIME_STEP_EVENT_BEGIN) { + for (StringRefNull particle_name : particle_names) { + context.influences.particle_time_step_begin_actions.add_as(particle_name, action); + } + } + else { + for (StringRefNull particle_name : particle_names) { + context.influences.particle_time_step_end_actions.add_as(particle_name, action); + } + } + } +} + +class SequenceParticleAction : public ParticleAction { private: - Array<std::string> names_; - std::string my_state_; - const fn::MultiFunction &inputs_fn_; - uint32_t seed_; + Vector<const ParticleAction *> actions_; public: - MyBasicEmitter(Array<std::string> names, - std::string my_state, - const fn::MultiFunction &inputs_fn, - uint32_t seed) - : names_(std::move(names)), - my_state_(std::move(my_state)), - inputs_fn_(inputs_fn), - seed_(seed) + SequenceParticleAction(Span<const ParticleAction *> actions) : actions_(std::move(actions)) { } - void emit(ParticleEmitterContext &context) const override + void execute(ParticleActionContext &context) const override { - auto *state = context.solve_context().state_map().lookup<ParticleMeshEmitterSimulationState>( - my_state_); - if (state == nullptr) { - return; + for (const ParticleAction *action : actions_) { + action->execute(context); } + } +}; - fn::MFContextBuilder mf_context; - mf_context.add_global_context("PersistentDataHandleMap", - &context.solve_context().handle_map()); +class SetParticleAttributeAction : public ParticleAction { + private: + std::string attribute_name_; + const CPPType &cpp_type_; + const ParticleFunction &inputs_fn_; - fn::MFParamsBuilder mf_params{inputs_fn_, 1}; - bke::PersistentObjectHandle object_handle; - float rate; - mf_params.add_uninitialized_single_output(&object_handle); - mf_params.add_uninitialized_single_output(&rate); - inputs_fn_.call(IndexRange(1), mf_params, mf_context); + public: + SetParticleAttributeAction(std::string attribute_name, + const CPPType &cpp_type, + const ParticleFunction &inputs_fn) + : attribute_name_(std::move(attribute_name)), cpp_type_(cpp_type), inputs_fn_(inputs_fn) + { + } - const Object *object = context.solve_context().handle_map().lookup(object_handle); - if (object == nullptr) { + void execute(ParticleActionContext &context) const override + { + std::optional<GMutableSpan> attribute_array = context.particles.attributes.try_get( + attribute_name_, cpp_type_); + if (!attribute_array.has_value()) { return; } - Vector<float3> new_positions; - Vector<float3> new_velocities; - Vector<float> new_birth_times; - - TimeInterval time_interval = context.simulation_time_interval(); - float start_time = time_interval.start(); - RandomNumberGenerator rng{(*(uint32_t *)&start_time) ^ seed_}; - - const float time_between_particles = 1.0f / rate; - while (state->last_birth_time + time_between_particles < time_interval.end()) { - new_positions.append(rng.get_unit_float3() * 0.3 + float3(object->loc)); - new_velocities.append(rng.get_unit_float3()); - const float birth_time = state->last_birth_time + time_between_particles; - new_birth_times.append(birth_time); - state->last_birth_time = birth_time; - } - - for (StringRef name : names_) { - ParticleAllocator *allocator = context.try_get_particle_allocator(name); - if (allocator == nullptr) { - return; - } - - int amount = new_positions.size(); - fn::MutableAttributesRef attributes = allocator->allocate(amount); + ParticleFunctionEvaluator evaluator{inputs_fn_, context.solve_context, context.particles}; + evaluator.compute(); + GVSpan values = evaluator.get(0); - initialized_copy_n(new_positions.data(), amount, attributes.get<float3>("Position").data()); - initialized_copy_n(new_velocities.data(), amount, attributes.get<float3>("Velocity").data()); - initialized_copy_n( - new_birth_times.data(), amount, attributes.get<float>("Birth Time").data()); + if (values.is_single_element()) { + cpp_type_.fill_initialized_indices( + values.as_single_element(), attribute_array->data(), context.particles.index_mask); + } + else { + GSpan value_array = values.as_full_array(); + cpp_type_.copy_to_initialized_indices( + value_array.data(), attribute_array->data(), context.particles.index_mask); } } }; -static Vector<const nodes::DNode *> find_linked_particle_simulations( - const nodes::DOutputSocket &output_socket) +static const ParticleAction *concatenate_actions(CollectContext &context, + Span<const ParticleAction *> actions) { - Vector<const nodes::DNode *> simulation_nodes; - for (const nodes::DInputSocket *target_socket : output_socket.linked_sockets()) { - if (target_socket->node().idname() == "SimulationNodeParticleSimulation") { - simulation_nodes.append(&target_socket->node()); + Vector<const ParticleAction *> non_null_actions; + for (const ParticleAction *action : actions) { + if (action != nullptr) { + non_null_actions.append(action); } } - return simulation_nodes; + if (non_null_actions.size() == 0) { + return nullptr; + } + if (non_null_actions.size() == 1) { + return non_null_actions[0]; + } + return &context.resources.construct<SequenceParticleAction>(AT, std::move(non_null_actions)); } -static ParticleEmitter *create_particle_emitter(const nodes::DNode &dnode, - ResourceCollector &resources, - nodes::MFNetworkTreeMap &network_map, - RequiredStates &r_required_states) +static const ParticleAction *create_set_particle_attribute_action( + CollectContext &context, const DOutputSocket &dsocket, Span<StringRefNull> particle_names) { - Vector<const nodes::DNode *> simulation_dnodes = find_linked_particle_simulations( - dnode.output(0)); - if (simulation_dnodes.size() == 0) { + const DNode &dnode = dsocket.node(); + MFInputSocket &name_socket = context.network_map.lookup_dummy(dnode.input(1)); + MFInputSocket &value_socket = name_socket.node().input(1); + std::optional<Array<std::string>> names = compute_global_string_inputs(context.network_map, + {&name_socket}); + if (!names.has_value()) { return nullptr; } - Array<std::string> names{simulation_dnodes.size()}; - for (int i : simulation_dnodes.index_range()) { - names[i] = dnode_to_path(*simulation_dnodes[i]); + std::string attribute_name = (*names)[0]; + const CPPType &attribute_type = value_socket.data_type().single_type(); + + const ParticleFunction *inputs_fn = create_particle_function_for_inputs(context, + {&value_socket}); + if (inputs_fn == nullptr) { + return nullptr; } - Array<const fn::MFInputSocket *> input_sockets{dnode.inputs().size()}; - for (int i : input_sockets.index_range()) { - input_sockets[i] = &network_map.lookup_dummy(dnode.input(i)); + for (StringRef particle_name : particle_names) { + context.influences.particle_attributes_builder.lookup_as(particle_name) + ->add(attribute_name, attribute_type); } - if (network_map.network().have_dummy_or_unlinked_dependencies(input_sockets)) { + ParticleAction &this_action = context.resources.construct<SetParticleAttributeAction>( + AT, attribute_name, attribute_type, *inputs_fn); + + const ParticleAction *previous_action = create_particle_action( + context, dnode.input(0), particle_names); + + return concatenate_actions(context, {previous_action, &this_action}); +} + +class ParticleConditionAction : public ParticleAction { + private: + const ParticleFunction &inputs_fn_; + const ParticleAction *action_true_; + const ParticleAction *action_false_; + + public: + ParticleConditionAction(const ParticleFunction &inputs_fn, + const ParticleAction *action_true, + const ParticleAction *action_false) + : inputs_fn_(inputs_fn), action_true_(action_true), action_false_(action_false) + { + } + + void execute(ParticleActionContext &context) const override + { + ParticleFunctionEvaluator evaluator{inputs_fn_, context.solve_context, context.particles}; + evaluator.compute(); + VSpan<bool> conditions = evaluator.get<bool>(0, "Condition"); + + if (conditions.is_single_element()) { + const bool condition = conditions.as_single_element(); + if (condition) { + if (action_true_ != nullptr) { + action_true_->execute(context); + } + } + else { + if (action_false_ != nullptr) { + action_false_->execute(context); + } + } + } + else { + Span<bool> conditions_array = conditions.as_full_array(); + + Vector<int64_t> true_indices; + Vector<int64_t> false_indices; + for (int i : context.particles.index_mask) { + if (conditions_array[i]) { + true_indices.append(i); + } + else { + false_indices.append(i); + } + } + + if (action_true_ != nullptr) { + ParticleChunkContext chunk_context{true_indices.as_span(), context.particles.attributes}; + ParticleActionContext action_context{context.solve_context, chunk_context}; + action_true_->execute(action_context); + } + if (action_false_ != nullptr) { + ParticleChunkContext chunk_context{false_indices.as_span(), context.particles.attributes}; + ParticleActionContext action_context{context.solve_context, chunk_context}; + action_false_->execute(action_context); + } + } + } +}; + +static const ParticleAction *create_particle_condition_action(CollectContext &context, + const DOutputSocket &dsocket, + Span<StringRefNull> particle_names) +{ + const DNode &dnode = dsocket.node(); + MFInputSocket &condition_socket = context.network_map.lookup_dummy(dnode.input(0)); + + const ParticleFunction *inputs_fn = create_particle_function_for_inputs(context, + {&condition_socket}); + if (inputs_fn == nullptr) { return nullptr; } - fn::MultiFunction &inputs_fn = resources.construct<fn::MFNetworkEvaluator>( - AT, Span<const fn::MFOutputSocket *>(), input_sockets.as_span()); + const ParticleAction *true_action = create_particle_action( + context, dnode.input(1), particle_names); + const ParticleAction *false_action = create_particle_action( + context, dnode.input(2), particle_names); - std::string my_state_name = dnode_to_path(dnode); - r_required_states.add(my_state_name, SIM_TYPE_NAME_PARTICLE_MESH_EMITTER); - uint32_t seed = DefaultHash<std::string>{}(my_state_name); - ParticleEmitter &emitter = resources.construct<MyBasicEmitter>( - AT, std::move(names), std::move(my_state_name), inputs_fn, seed); - return &emitter; + if (true_action == nullptr && false_action == nullptr) { + return nullptr; + } + return &context.resources.construct<ParticleConditionAction>( + AT, *inputs_fn, true_action, false_action); } -static void collect_emitters(nodes::MFNetworkTreeMap &network_map, - ResourceCollector &resources, - SimulationInfluences &r_influences, - RequiredStates &r_required_states) +static const ParticleAction *create_particle_action(CollectContext &context, + const DOutputSocket &dsocket, + Span<StringRefNull> particle_names) { - for (const nodes::DNode *dnode : - network_map.tree().nodes_by_type("SimulationNodeParticleMeshEmitter")) { - ParticleEmitter *emitter = create_particle_emitter( - *dnode, resources, network_map, r_required_states); - if (emitter != nullptr) { - r_influences.particle_emitters.append(emitter); - } + const DNode &dnode = dsocket.node(); + if (dnode.idname() == "SimulationNodeSetParticleAttribute") { + return create_set_particle_attribute_action(context, dsocket, particle_names); + } + if (dnode.idname() == "SimulationNodeExecuteCondition") { + return create_particle_condition_action(context, dsocket, particle_names); } + return nullptr; } -static void prepare_particle_attribute_builders(nodes::MFNetworkTreeMap &network_map, - ResourceCollector &resources, - SimulationInfluences &r_influences) +static void initialize_particle_attribute_builders(CollectContext &context) { - for (const nodes::DNode *dnode : get_particle_simulation_nodes(network_map.tree())) { - std::string name = dnode_to_path(*dnode); - fn::AttributesInfoBuilder &builder = resources.construct<fn::AttributesInfoBuilder>(AT); - builder.add<float3>("Position", {0, 0, 0}); - builder.add<float3>("Velocity", {0, 0, 0}); - builder.add<int>("ID", 0); + for (const DNode *dnode : context.particle_simulation_nodes) { + StringRef name = get_identifier(context, *dnode); + AttributesInfoBuilder &attributes_builder = context.resources.construct<AttributesInfoBuilder>( + AT); + attributes_builder.add<float3>("Position", {0, 0, 0}); + attributes_builder.add<float3>("Velocity", {0, 0, 0}); + attributes_builder.add<int>("ID", 0); /* TODO: Use bool property, but need to add CD_PROP_BOOL first. */ - builder.add<int>("Dead", 0); - builder.add<float>("Birth Time", 0.0f); - r_influences.particle_attributes_builder.add_new(std::move(name), &builder); + attributes_builder.add<int>("Dead", 0); + /* TODO: Use uint32_t, but we don't have a corresponding custom property type. */ + attributes_builder.add<int>("Hash", 0); + attributes_builder.add<float>("Birth Time", 0.0f); + context.influences.particle_attributes_builder.add_new(name, &attributes_builder); } } -static void find_used_data_blocks(const nodes::DerivedNodeTree &tree, - SimulationInfluences &r_influences) +static void optimize_function_network(CollectContext &context) { - const bNodeSocketType *socktype = nodeSocketTypeFind("NodeSocketObject"); - BLI_assert(socktype != nullptr); - - for (const nodes::DInputSocket *dsocket : tree.input_sockets()) { - const bNodeSocket *bsocket = dsocket->bsocket(); - if (bsocket->typeinfo == socktype) { - Object *value = ((const bNodeSocketValueObject *)bsocket->default_value)->value; - if (value != nullptr) { - r_influences.used_data_blocks.add(&value->id); - } - } - } + fn::mf_network_optimization::constant_folding(context.network, context.resources); + fn::mf_network_optimization::common_subnetwork_elimination(context.network); + fn::mf_network_optimization::dead_node_removal(context.network); + // WM_clipboard_text_set(network.to_dot().c_str(), false); } void collect_simulation_influences(Simulation &simulation, @@ -456,30 +757,28 @@ void collect_simulation_influences(Simulation &simulation, SimulationInfluences &r_influences, RequiredStates &r_required_states) { - nodes::NodeTreeRefMap tree_refs; - const nodes::DerivedNodeTree tree{simulation.nodetree, tree_refs}; + NodeTreeRefMap tree_refs; + const DerivedNodeTree tree{simulation.nodetree, tree_refs}; - fn::MFNetwork &network = resources.construct<fn::MFNetwork>(AT); - nodes::MFNetworkTreeMap network_map = insert_node_tree_into_mf_network(network, tree, resources); + MFNetwork &network = resources.construct<MFNetwork>(AT); + MFNetworkTreeMap network_map = insert_node_tree_into_mf_network(network, tree, resources); - prepare_particle_attribute_builders(network_map, resources, r_influences); + CollectContext context{r_influences, r_required_states, resources, network_map}; + initialize_particle_attribute_builders(context); - DummyDataSources data_sources; - find_and_deduplicate_particle_attribute_nodes(network_map, data_sources); + prepare_particle_attribute_nodes(context); + prepare_time_input_nodes(context); - fn::mf_network_optimization::constant_folding(network, resources); - fn::mf_network_optimization::common_subnetwork_elimination(network); - fn::mf_network_optimization::dead_node_removal(network); - // WM_clipboard_text_set(network.to_dot().c_str(), false); + collect_forces(context); + collect_emitters(context); + collect_birth_events(context); + collect_time_step_events(context); - collect_forces(network_map, resources, data_sources, r_influences); - collect_emitters(network_map, resources, r_influences, r_required_states); + optimize_function_network(context); - for (const nodes::DNode *dnode : get_particle_simulation_nodes(tree)) { - r_required_states.add(dnode_to_path(*dnode), SIM_TYPE_NAME_PARTICLE_SIMULATION); + for (const DNode *dnode : context.particle_simulation_nodes) { + r_required_states.add(get_identifier(context, *dnode), SIM_TYPE_NAME_PARTICLE_SIMULATION); } - - find_used_data_blocks(tree, r_influences); } } // namespace blender::sim diff --git a/source/blender/simulation/intern/simulation_collect_influences.hh b/source/blender/simulation/intern/simulation_collect_influences.hh index 42cbea6977e..caf5a8c4ffa 100644 --- a/source/blender/simulation/intern/simulation_collect_influences.hh +++ b/source/blender/simulation/intern/simulation_collect_influences.hh @@ -21,7 +21,7 @@ #include "BLI_resource_collector.hh" -#include "simulation_solver.hh" +#include "simulation_solver_influences.hh" namespace blender::sim { diff --git a/source/blender/simulation/intern/simulation_solver.cc b/source/blender/simulation/intern/simulation_solver.cc index ee7a8d40035..3c159eb1c58 100644 --- a/source/blender/simulation/intern/simulation_solver.cc +++ b/source/blender/simulation/intern/simulation_solver.cc @@ -17,23 +17,16 @@ #include "simulation_solver.hh" #include "BKE_customdata.h" -#include "BKE_lib_id.h" #include "BKE_persistent_data_handle.hh" #include "BLI_rand.hh" #include "BLI_set.hh" -namespace blender::sim { - -ParticleForce::~ParticleForce() -{ -} +#include "DEG_depsgraph_query.h" -ParticleEmitter::~ParticleEmitter() -{ -} +namespace blender::sim { -static CustomDataType cpp_to_custom_data_type(const fn::CPPType &type) +static CustomDataType cpp_to_custom_data_type(const CPPType &type) { if (type.is<float3>()) { return CD_PROP_FLOAT3; @@ -48,18 +41,18 @@ static CustomDataType cpp_to_custom_data_type(const fn::CPPType &type) return CD_PROP_FLOAT; } -static const fn::CPPType &custom_to_cpp_data_type(CustomDataType type) +static const CPPType &custom_to_cpp_data_type(CustomDataType type) { switch (type) { case CD_PROP_FLOAT3: - return fn::CPPType::get<float3>(); + return CPPType::get<float3>(); case CD_PROP_FLOAT: - return fn::CPPType::get<float>(); + return CPPType::get<float>(); case CD_PROP_INT32: - return fn::CPPType::get<int32_t>(); + return CPPType::get<int32_t>(); default: BLI_assert(false); - return fn::CPPType::get<float>(); + return CPPType::get<float>(); } } @@ -67,33 +60,33 @@ class CustomDataAttributesRef { private: Array<void *> buffers_; int64_t size_; - const fn::AttributesInfo &info_; + const AttributesInfo &info_; public: - CustomDataAttributesRef(CustomData &custom_data, int64_t size, const fn::AttributesInfo &info) + CustomDataAttributesRef(CustomData &custom_data, int64_t size, const AttributesInfo &info) : buffers_(info.size(), nullptr), size_(size), info_(info) { for (int attribute_index : info.index_range()) { StringRefNull name = info.name_of(attribute_index); - const fn::CPPType &cpp_type = info.type_of(attribute_index); + const CPPType &cpp_type = info.type_of(attribute_index); CustomDataType custom_type = cpp_to_custom_data_type(cpp_type); void *data = CustomData_get_layer_named(&custom_data, custom_type, name.c_str()); buffers_[attribute_index] = data; } } - operator fn::MutableAttributesRef() + operator MutableAttributesRef() { - return fn::MutableAttributesRef(info_, buffers_, size_); + return MutableAttributesRef(info_, buffers_, size_); } - operator fn::AttributesRef() const + operator AttributesRef() const { - return fn::AttributesRef(info_, buffers_, size_); + return AttributesRef(info_, buffers_, size_); } }; -static void ensure_attributes_exist(ParticleSimulationState *state, const fn::AttributesInfo &info) +static void ensure_attributes_exist(ParticleSimulationState *state, const AttributesInfo &info) { bool found_layer_to_remove; do { @@ -101,7 +94,7 @@ static void ensure_attributes_exist(ParticleSimulationState *state, const fn::At for (int layer_index = 0; layer_index < state->attributes.totlayer; layer_index++) { CustomDataLayer *layer = &state->attributes.layers[layer_index]; BLI_assert(layer->name != nullptr); - const fn::CPPType &cpp_type = custom_to_cpp_data_type((CustomDataType)layer->type); + const CPPType &cpp_type = custom_to_cpp_data_type((CustomDataType)layer->type); StringRefNull name = layer->name; if (!info.has_attribute(name, cpp_type)) { found_layer_to_remove = true; @@ -113,7 +106,7 @@ static void ensure_attributes_exist(ParticleSimulationState *state, const fn::At for (int attribute_index : info.index_range()) { StringRefNull attribute_name = info.name_of(attribute_index); - const fn::CPPType &cpp_type = info.type_of(attribute_index); + const CPPType &cpp_type = info.type_of(attribute_index); CustomDataType custom_type = cpp_to_custom_data_type(cpp_type); if (CustomData_get_layer_named(&state->attributes, custom_type, attribute_name.c_str()) == nullptr) { @@ -128,35 +121,38 @@ static void ensure_attributes_exist(ParticleSimulationState *state, const fn::At } } -BLI_NOINLINE static void simulate_existing_particles(SimulationSolveContext &solve_context, - ParticleSimulationState &state, - const fn::AttributesInfo &attributes_info) +BLI_NOINLINE static void simulate_particle_chunk(SimulationSolveContext &solve_context, + ParticleSimulationState &state, + MutableAttributesRef attributes, + MutableSpan<float> remaining_durations, + float end_time) { - CustomDataAttributesRef custom_data_attributes{ - state.attributes, state.tot_particles, attributes_info}; - fn::MutableAttributesRef attributes = custom_data_attributes; - - Array<float3> force_vectors{state.tot_particles, {0, 0, 0}}; - const Vector<const ParticleForce *> *forces = - solve_context.influences().particle_forces.lookup_ptr(state.head.name); - - if (forces != nullptr) { - ParticleChunkContext particle_chunk_context{IndexMask(state.tot_particles), attributes}; - ParticleForceContext particle_force_context{ - solve_context, particle_chunk_context, force_vectors}; + int particle_amount = attributes.size(); + + Span<const ParticleAction *> begin_actions = + solve_context.influences.particle_time_step_begin_actions.lookup_as(state.head.name); + for (const ParticleAction *action : begin_actions) { + ParticleChunkContext particles{IndexMask(particle_amount), attributes}; + ParticleActionContext action_context{solve_context, particles}; + action->execute(action_context); + } - for (const ParticleForce *force : *forces) { - force->add_force(particle_force_context); - } + Array<float3> force_vectors{particle_amount, {0, 0, 0}}; + Span<const ParticleForce *> forces = solve_context.influences.particle_forces.lookup_as( + state.head.name); + for (const ParticleForce *force : forces) { + ParticleChunkContext particles{IndexMask(particle_amount), attributes}; + ParticleForceContext particle_force_context{solve_context, particles, force_vectors}; + force->add_force(particle_force_context); } MutableSpan<float3> positions = attributes.get<float3>("Position"); MutableSpan<float3> velocities = attributes.get<float3>("Velocity"); MutableSpan<float> birth_times = attributes.get<float>("Birth Time"); MutableSpan<int> dead_states = attributes.get<int>("Dead"); - float end_time = solve_context.solve_interval().end(); - float time_step = solve_context.solve_interval().duration(); - for (int i : positions.index_range()) { + + for (int i : IndexRange(particle_amount)) { + const float time_step = remaining_durations[i]; velocities[i] += force_vectors[i] * time_step; positions[i] += velocities[i] * time_step; @@ -164,14 +160,35 @@ BLI_NOINLINE static void simulate_existing_particles(SimulationSolveContext &sol dead_states[i] = true; } } + + Span<const ParticleAction *> end_actions = + solve_context.influences.particle_time_step_end_actions.lookup_as(state.head.name); + for (const ParticleAction *action : end_actions) { + ParticleChunkContext particles{IndexMask(particle_amount), attributes}; + ParticleActionContext action_context{solve_context, particles}; + action->execute(action_context); + } +} + +BLI_NOINLINE static void simulate_existing_particles(SimulationSolveContext &solve_context, + ParticleSimulationState &state, + const AttributesInfo &attributes_info) +{ + CustomDataAttributesRef custom_data_attributes{ + state.attributes, state.tot_particles, attributes_info}; + MutableAttributesRef attributes = custom_data_attributes; + + Array<float> remaining_durations(state.tot_particles, solve_context.solve_interval.duration()); + simulate_particle_chunk( + solve_context, state, attributes, remaining_durations, solve_context.solve_interval.stop()); } BLI_NOINLINE static void run_emitters(SimulationSolveContext &solve_context, ParticleAllocators &particle_allocators) { - for (const ParticleEmitter *emitter : solve_context.influences().particle_emitters) { + for (const ParticleEmitter *emitter : solve_context.influences.particle_emitters) { ParticleEmitterContext emitter_context{ - solve_context, particle_allocators, solve_context.solve_interval()}; + solve_context, particle_allocators, solve_context.solve_interval}; emitter->emit(emitter_context); } } @@ -181,10 +198,10 @@ BLI_NOINLINE static int count_particles_after_time_step(ParticleSimulationState { CustomDataAttributesRef custom_data_attributes{ state.attributes, state.tot_particles, allocator.attributes_info()}; - fn::MutableAttributesRef attributes = custom_data_attributes; + MutableAttributesRef attributes = custom_data_attributes; int new_particle_amount = attributes.get<int>("Dead").count(0); - for (fn::MutableAttributesRef emitted_attributes : allocator.get_allocations()) { + for (MutableAttributesRef emitted_attributes : allocator.get_allocations()) { new_particle_amount += emitted_attributes.get<int>("Dead").count(0); } @@ -199,7 +216,7 @@ BLI_NOINLINE static void remove_dead_and_add_new_particles(ParticleSimulationSta CustomDataAttributesRef custom_data_attributes{ state.attributes, state.tot_particles, allocator.attributes_info()}; - Vector<fn::MutableAttributesRef> particle_sources; + Vector<MutableAttributesRef> particle_sources; particle_sources.append(custom_data_attributes); particle_sources.extend(allocator.get_allocations()); @@ -211,16 +228,16 @@ BLI_NOINLINE static void remove_dead_and_add_new_particles(ParticleSimulationSta dead_layer = &layer; continue; } - const fn::CPPType &cpp_type = custom_to_cpp_data_type((CustomDataType)layer.type); - fn::GMutableSpan new_buffer{ + const CPPType &cpp_type = custom_to_cpp_data_type((CustomDataType)layer.type); + GMutableSpan new_buffer{ cpp_type, MEM_mallocN_aligned(new_particle_amount * cpp_type.size(), cpp_type.alignment(), AT), new_particle_amount}; int current = 0; - for (fn::MutableAttributesRef attributes : particle_sources) { + for (MutableAttributesRef attributes : particle_sources) { Span<int> dead_states = attributes.get<int>("Dead"); - fn::GSpan source_buffer = attributes.get(name); + GSpan source_buffer = attributes.get(name); BLI_assert(source_buffer.type() == cpp_type); for (int i : attributes.index_range()) { if (dead_states[i] == 0) { @@ -233,7 +250,7 @@ BLI_NOINLINE static void remove_dead_and_add_new_particles(ParticleSimulationSta if (layer.data != nullptr) { MEM_freeN(layer.data); } - layer.data = new_buffer.buffer(); + layer.data = new_buffer.data(); } BLI_assert(dead_layer != nullptr); @@ -246,56 +263,10 @@ BLI_NOINLINE static void remove_dead_and_add_new_particles(ParticleSimulationSta state.next_particle_id += allocator.total_allocated(); } -static void update_persistent_data_handles(Simulation &simulation, - const VectorSet<ID *> &used_data_blocks) -{ - Set<ID *> contained_ids; - Set<int> used_handles; - - /* Remove handles that have been invalidated. */ - LISTBASE_FOREACH_MUTABLE ( - PersistentDataHandleItem *, handle_item, &simulation.persistent_data_handles) { - if (handle_item->id == nullptr) { - BLI_remlink(&simulation.persistent_data_handles, handle_item); - continue; - } - if (!used_data_blocks.contains(handle_item->id)) { - id_us_min(handle_item->id); - BLI_remlink(&simulation.persistent_data_handles, handle_item); - MEM_freeN(handle_item); - continue; - } - contained_ids.add_new(handle_item->id); - used_handles.add_new(handle_item->handle); - } - - /* Add new handles that are not in the list yet. */ - int next_handle = 0; - for (ID *id : used_data_blocks) { - if (contained_ids.contains(id)) { - continue; - } - - /* Find the next available handle. */ - while (used_handles.contains(next_handle)) { - next_handle++; - } - used_handles.add_new(next_handle); - - PersistentDataHandleItem *handle_item = (PersistentDataHandleItem *)MEM_callocN( - sizeof(*handle_item), AT); - /* Cannot store const pointers in DNA. */ - id_us_plus(id); - handle_item->id = id; - handle_item->handle = next_handle; - - BLI_addtail(&simulation.persistent_data_handles, handle_item); - } -} - void initialize_simulation_states(Simulation &simulation, Depsgraph &UNUSED(depsgraph), - const SimulationInfluences &UNUSED(influences)) + const SimulationInfluences &UNUSED(influences), + const bke::PersistentDataHandleMap &UNUSED(handle_map)) { simulation.current_simulation_time = 0.0f; } @@ -303,15 +274,10 @@ void initialize_simulation_states(Simulation &simulation, void solve_simulation_time_step(Simulation &simulation, Depsgraph &depsgraph, const SimulationInfluences &influences, + const bke::PersistentDataHandleMap &handle_map, + const DependencyAnimations &dependency_animations, float time_step) { - update_persistent_data_handles(simulation, influences.used_data_blocks); - - bke::PersistentDataHandleMap handle_map; - LISTBASE_FOREACH (PersistentDataHandleItem *, handle, &simulation.persistent_data_handles) { - handle_map.add(handle->handle, *handle->id); - } - SimulationStateMap state_map; LISTBASE_FOREACH (SimulationState *, state, &simulation.states) { state_map.add(state); @@ -322,30 +288,32 @@ void solve_simulation_time_step(Simulation &simulation, influences, TimeInterval(simulation.current_simulation_time, time_step), state_map, - handle_map}; - TimeInterval simulation_time_interval{simulation.current_simulation_time, time_step}; + handle_map, + dependency_animations}; Span<ParticleSimulationState *> particle_simulation_states = state_map.lookup<ParticleSimulationState>(); - Map<std::string, std::unique_ptr<fn::AttributesInfo>> attribute_infos; + Map<std::string, std::unique_ptr<AttributesInfo>> attribute_infos; Map<std::string, std::unique_ptr<ParticleAllocator>> particle_allocators_map; for (ParticleSimulationState *state : particle_simulation_states) { - const fn::AttributesInfoBuilder &builder = *influences.particle_attributes_builder.lookup_as( + const AttributesInfoBuilder &builder = *influences.particle_attributes_builder.lookup_as( state->head.name); - auto info = std::make_unique<fn::AttributesInfo>(builder); + auto info = std::make_unique<AttributesInfo>(builder); ensure_attributes_exist(state, *info); + uint32_t hash_seed = DefaultHash<StringRef>{}(state->head.name); particle_allocators_map.add_new( - state->head.name, std::make_unique<ParticleAllocator>(*info, state->next_particle_id)); + state->head.name, + std::make_unique<ParticleAllocator>(*info, state->next_particle_id, hash_seed)); attribute_infos.add_new(state->head.name, std::move(info)); } ParticleAllocators particle_allocators{particle_allocators_map}; for (ParticleSimulationState *state : particle_simulation_states) { - const fn::AttributesInfo &attributes_info = *attribute_infos.lookup_as(state->head.name); + const AttributesInfo &attributes_info = *attribute_infos.lookup_as(state->head.name); simulate_existing_particles(solve_context, *state, attributes_info); } @@ -353,10 +321,35 @@ void solve_simulation_time_step(Simulation &simulation, for (ParticleSimulationState *state : particle_simulation_states) { ParticleAllocator &allocator = *particle_allocators.try_get_allocator(state->head.name); + + for (MutableAttributesRef attributes : allocator.get_allocations()) { + Span<const ParticleAction *> actions = influences.particle_birth_actions.lookup_as( + state->head.name); + for (const ParticleAction *action : actions) { + ParticleChunkContext chunk_context{IndexRange(attributes.size()), attributes}; + ParticleActionContext action_context{solve_context, chunk_context}; + action->execute(action_context); + } + } + } + + for (ParticleSimulationState *state : particle_simulation_states) { + ParticleAllocator &allocator = *particle_allocators.try_get_allocator(state->head.name); + + for (MutableAttributesRef attributes : allocator.get_allocations()) { + Array<float> remaining_durations(attributes.size()); + Span<float> birth_times = attributes.get<float>("Birth Time"); + const float end_time = solve_context.solve_interval.stop(); + for (int i : attributes.index_range()) { + remaining_durations[i] = end_time - birth_times[i]; + } + simulate_particle_chunk(solve_context, *state, attributes, remaining_durations, end_time); + } + remove_dead_and_add_new_particles(*state, allocator); } - simulation.current_simulation_time = simulation_time_interval.end(); + simulation.current_simulation_time = solve_context.solve_interval.stop(); } } // namespace blender::sim diff --git a/source/blender/simulation/intern/simulation_solver.hh b/source/blender/simulation/intern/simulation_solver.hh index 2dc0921ad9e..9d30ea87731 100644 --- a/source/blender/simulation/intern/simulation_solver.hh +++ b/source/blender/simulation/intern/simulation_solver.hh @@ -17,259 +17,22 @@ #ifndef __SIM_SIMULATION_SOLVER_HH__ #define __SIM_SIMULATION_SOLVER_HH__ -#include "BLI_float3.hh" -#include "BLI_span.hh" - -#include "DNA_simulation_types.h" - -#include "FN_attributes_ref.hh" - -#include "BKE_persistent_data_handle.hh" -#include "BKE_simulation.h" - -#include "particle_allocator.hh" -#include "time_interval.hh" +#include "simulation_collect_influences.hh" struct Depsgraph; namespace blender::sim { -class ParticleEmitterContext; -class ParticleForceContext; - -class ParticleEmitter { - public: - virtual ~ParticleEmitter(); - virtual void emit(ParticleEmitterContext &context) const = 0; -}; - -class ParticleForce { - public: - virtual ~ParticleForce(); - virtual void add_force(ParticleForceContext &context) const = 0; -}; - -struct SimulationInfluences { - Map<std::string, Vector<const ParticleForce *>> particle_forces; - Map<std::string, fn::AttributesInfoBuilder *> particle_attributes_builder; - Vector<const ParticleEmitter *> particle_emitters; - VectorSet<ID *> used_data_blocks; -}; - -class SimulationStateMap { - private: - Map<StringRefNull, SimulationState *> states_by_name_; - Map<StringRefNull, Vector<SimulationState *>> states_by_type_; - - public: - void add(SimulationState *state) - { - states_by_name_.add_new(state->name, state); - states_by_type_.lookup_or_add_default(state->type).append(state); - } - - template<typename StateType> StateType *lookup(StringRef name) const - { - const char *type = BKE_simulation_get_state_type_name<StateType>(); - return (StateType *)this->lookup_name_type(name, type); - } - - template<typename StateType> Span<StateType *> lookup() const - { - const char *type = BKE_simulation_get_state_type_name<StateType>(); - return this->lookup_type(type).cast<StateType *>(); - } - - SimulationState *lookup_name_type(StringRef name, StringRef type) const - { - SimulationState *state = states_by_name_.lookup_default_as(name, nullptr); - if (state == nullptr) { - return nullptr; - } - if (state->type == type) { - return state; - } - return nullptr; - } - - Span<SimulationState *> lookup_type(StringRef type) const - { - const Vector<SimulationState *> *states = states_by_type_.lookup_ptr_as(type); - if (states == nullptr) { - return {}; - } - else { - return states->as_span(); - } - } -}; - -class SimulationSolveContext { - private: - Simulation &simulation_; - Depsgraph &depsgraph_; - const SimulationInfluences &influences_; - TimeInterval solve_interval_; - const SimulationStateMap &state_map_; - const bke::PersistentDataHandleMap &id_handle_map_; - - public: - SimulationSolveContext(Simulation &simulation, - Depsgraph &depsgraph, - const SimulationInfluences &influences, - TimeInterval solve_interval, - const SimulationStateMap &state_map, - const bke::PersistentDataHandleMap &handle_map) - : simulation_(simulation), - depsgraph_(depsgraph), - influences_(influences), - solve_interval_(solve_interval), - state_map_(state_map), - id_handle_map_(handle_map) - { - } - - TimeInterval solve_interval() const - { - return solve_interval_; - } - - const SimulationInfluences &influences() const - { - return influences_; - } - - const bke::PersistentDataHandleMap &handle_map() const - { - return id_handle_map_; - } - - const SimulationStateMap &state_map() const - { - return state_map_; - } -}; - -class ParticleAllocators { - private: - Map<std::string, std::unique_ptr<ParticleAllocator>> &allocators_; - - public: - ParticleAllocators(Map<std::string, std::unique_ptr<ParticleAllocator>> &allocators) - : allocators_(allocators) - { - } - - ParticleAllocator *try_get_allocator(StringRef particle_simulation_name) - { - auto *ptr = allocators_.lookup_ptr_as(particle_simulation_name); - if (ptr != nullptr) { - return ptr->get(); - } - else { - return nullptr; - } - } -}; - -class ParticleChunkContext { - private: - IndexMask index_mask_; - fn::MutableAttributesRef attributes_; - - public: - ParticleChunkContext(IndexMask index_mask, fn::MutableAttributesRef attributes) - : index_mask_(index_mask), attributes_(attributes) - { - } - - IndexMask index_mask() const - { - return index_mask_; - } - - fn::MutableAttributesRef attributes() - { - return attributes_; - } - - fn::AttributesRef attributes() const - { - return attributes_; - } -}; - -class ParticleEmitterContext { - private: - SimulationSolveContext &solve_context_; - ParticleAllocators &particle_allocators_; - TimeInterval simulation_time_interval_; - - public: - ParticleEmitterContext(SimulationSolveContext &solve_context, - ParticleAllocators &particle_allocators, - TimeInterval simulation_time_interval) - : solve_context_(solve_context), - particle_allocators_(particle_allocators), - simulation_time_interval_(simulation_time_interval) - { - } - - SimulationSolveContext &solve_context() - { - return solve_context_; - } - - ParticleAllocator *try_get_particle_allocator(StringRef particle_simulation_name) - { - return particle_allocators_.try_get_allocator(particle_simulation_name); - } - - TimeInterval simulation_time_interval() const - { - return simulation_time_interval_; - } -}; - -class ParticleForceContext { - private: - SimulationSolveContext &solve_context_; - const ParticleChunkContext &particle_chunk_context_; - MutableSpan<float3> force_dst_; - - public: - ParticleForceContext(SimulationSolveContext &solve_context, - const ParticleChunkContext &particle_chunk_context, - MutableSpan<float3> force_dst) - : solve_context_(solve_context), - particle_chunk_context_(particle_chunk_context), - force_dst_(force_dst) - { - } - - SimulationSolveContext &solve_context() - { - return solve_context_; - } - - const ParticleChunkContext &particle_chunk() const - { - return particle_chunk_context_; - } - - MutableSpan<float3> force_dst() - { - return force_dst_; - } -}; - void initialize_simulation_states(Simulation &simulation, Depsgraph &depsgraph, - const SimulationInfluences &influences); + const SimulationInfluences &influences, + const bke::PersistentDataHandleMap &handle_map); void solve_simulation_time_step(Simulation &simulation, Depsgraph &depsgraph, const SimulationInfluences &influences, + const bke::PersistentDataHandleMap &handle_map, + const DependencyAnimations &dependency_animations, float time_step); } // namespace blender::sim diff --git a/source/blender/simulation/intern/simulation_solver_influences.cc b/source/blender/simulation/intern/simulation_solver_influences.cc new file mode 100644 index 00000000000..75c2c820651 --- /dev/null +++ b/source/blender/simulation/intern/simulation_solver_influences.cc @@ -0,0 +1,53 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "simulation_solver_influences.hh" + +#include "DNA_object_types.h" + +namespace blender::sim { + +ParticleForce::~ParticleForce() +{ +} + +ParticleEmitter::~ParticleEmitter() +{ +} + +ParticleAction::~ParticleAction() +{ +} + +DependencyAnimations::~DependencyAnimations() +{ +} + +bool DependencyAnimations::is_object_transform_changing(Object &UNUSED(object)) const +{ + return false; +} + +void DependencyAnimations::get_object_transforms(Object &object, + Span<float> simulation_times, + MutableSpan<float4x4> r_transforms) const +{ + assert_same_size(simulation_times, r_transforms); + float4x4 world_matrix = object.obmat; + r_transforms.fill(world_matrix); +} + +} // namespace blender::sim diff --git a/source/blender/simulation/intern/simulation_solver_influences.hh b/source/blender/simulation/intern/simulation_solver_influences.hh new file mode 100644 index 00000000000..6633c1ebb1d --- /dev/null +++ b/source/blender/simulation/intern/simulation_solver_influences.hh @@ -0,0 +1,194 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __SIM_SIMULATION_SOLVER_INFLUENCES_HH__ +#define __SIM_SIMULATION_SOLVER_INFLUENCES_HH__ + +#include "BLI_float3.hh" +#include "BLI_float4x4.hh" +#include "BLI_multi_value_map.hh" +#include "BLI_span.hh" + +#include "DNA_simulation_types.h" + +#include "FN_attributes_ref.hh" + +#include "BKE_persistent_data_handle.hh" +#include "BKE_simulation.h" + +#include "particle_allocator.hh" +#include "time_interval.hh" + +namespace blender::sim { + +using fn::AttributesInfo; +using fn::AttributesInfoBuilder; +using fn::AttributesRef; +using fn::CPPType; +using fn::GMutableSpan; +using fn::GSpan; +using fn::MutableAttributesRef; + +class ParticleEmitterContext; +class ParticleForceContext; +class ParticleActionContext; + +class ParticleEmitter { + public: + virtual ~ParticleEmitter(); + virtual void emit(ParticleEmitterContext &context) const = 0; +}; + +class ParticleForce { + public: + virtual ~ParticleForce(); + virtual void add_force(ParticleForceContext &context) const = 0; +}; + +class ParticleAction { + public: + virtual ~ParticleAction(); + virtual void execute(ParticleActionContext &context) const = 0; +}; + +struct SimulationInfluences { + MultiValueMap<std::string, const ParticleForce *> particle_forces; + MultiValueMap<std::string, const ParticleAction *> particle_birth_actions; + MultiValueMap<std::string, const ParticleAction *> particle_time_step_begin_actions; + MultiValueMap<std::string, const ParticleAction *> particle_time_step_end_actions; + Map<std::string, AttributesInfoBuilder *> particle_attributes_builder; + Vector<const ParticleEmitter *> particle_emitters; +}; + +class SimulationStateMap { + private: + Map<StringRefNull, SimulationState *> states_by_name_; + MultiValueMap<StringRefNull, SimulationState *> states_by_type_; + + public: + void add(SimulationState *state) + { + states_by_name_.add_new(state->name, state); + states_by_type_.add(state->type, state); + } + + template<typename StateType> StateType *lookup(StringRef name) const + { + const char *type = BKE_simulation_get_state_type_name<StateType>(); + return (StateType *)this->lookup_name_type(name, type); + } + + template<typename StateType> Span<StateType *> lookup() const + { + const char *type = BKE_simulation_get_state_type_name<StateType>(); + return this->lookup_type(type).cast<StateType *>(); + } + + SimulationState *lookup_name_type(StringRef name, StringRef type) const + { + SimulationState *state = states_by_name_.lookup_default_as(name, nullptr); + if (state == nullptr) { + return nullptr; + } + if (state->type == type) { + return state; + } + return nullptr; + } + + Span<SimulationState *> lookup_type(StringRef type) const + { + return states_by_type_.lookup_as(type); + } +}; + +class DependencyAnimations { + public: + ~DependencyAnimations(); + + virtual bool is_object_transform_changing(Object &object) const; + virtual void get_object_transforms(Object &object, + Span<float> simulation_times, + MutableSpan<float4x4> r_transforms) const; +}; + +struct SimulationSolveContext { + Simulation &simulation; + Depsgraph &depsgraph; + const SimulationInfluences &influences; + TimeInterval solve_interval; + const SimulationStateMap &state_map; + const bke::PersistentDataHandleMap &handle_map; + const DependencyAnimations &dependency_animations; +}; + +class ParticleAllocators { + private: + Map<std::string, std::unique_ptr<ParticleAllocator>> &allocators_; + + public: + ParticleAllocators(Map<std::string, std::unique_ptr<ParticleAllocator>> &allocators) + : allocators_(allocators) + { + } + + ParticleAllocator *try_get_allocator(StringRef particle_simulation_name) + { + auto *ptr = allocators_.lookup_ptr_as(particle_simulation_name); + if (ptr != nullptr) { + return ptr->get(); + } + else { + return nullptr; + } + } +}; + +struct ParticleChunkContext { + IndexMask index_mask; + MutableAttributesRef attributes; +}; + +struct ParticleEmitterContext { + SimulationSolveContext &solve_context; + ParticleAllocators &particle_allocators; + TimeInterval emit_interval; + + template<typename StateType> StateType *lookup_state(StringRef name) + { + return solve_context.state_map.lookup<StateType>(name); + } + + ParticleAllocator *try_get_particle_allocator(StringRef particle_simulation_name) + { + return particle_allocators.try_get_allocator(particle_simulation_name); + } +}; + +struct ParticleForceContext { + SimulationSolveContext &solve_context; + ParticleChunkContext &particles; + MutableSpan<float3> force_dst; +}; + +struct ParticleActionContext { + SimulationSolveContext &solve_context; + ParticleChunkContext &particles; +}; + +} // namespace blender::sim + +#endif /* __SIM_SIMULATION_SOLVER_INFLUENCES_HH__ */ diff --git a/source/blender/simulation/intern/simulation_update.cc b/source/blender/simulation/intern/simulation_update.cc index 09219e0238f..32b582977d0 100644 --- a/source/blender/simulation/intern/simulation_update.cc +++ b/source/blender/simulation/intern/simulation_update.cc @@ -17,8 +17,11 @@ #include "SIM_simulation_update.hh" #include "BKE_customdata.h" +#include "BKE_lib_id.h" +#include "BKE_object.h" #include "BKE_simulation.h" +#include "DNA_modifier_types.h" #include "DNA_scene_types.h" #include "DNA_simulation_types.h" @@ -29,8 +32,11 @@ #include "BLI_listbase.h" #include "BLI_map.hh" #include "BLI_rand.h" +#include "BLI_set.hh" #include "BLI_vector.hh" +#include "NOD_node_tree_dependencies.hh" + #include "particle_function.hh" #include "simulation_collect_influences.hh" #include "simulation_solver.hh" @@ -88,6 +94,131 @@ static void update_simulation_state_list(Simulation *simulation, add_missing_states(simulation, required_states); } +class SampledDependencyAnimations : public DependencyAnimations { + private: + TimeInterval simulation_time_interval_; + MultiValueMap<Object *, float4x4> object_transforms_cache_; + + public: + SampledDependencyAnimations(TimeInterval simulation_time_interval) + : simulation_time_interval_(simulation_time_interval) + { + } + + void add_object_transforms(Object &object, Span<float4x4> transforms) + { + object_transforms_cache_.add_multiple(&object, transforms); + } + + bool is_object_transform_changing(Object &object) const + { + return object_transforms_cache_.lookup(&object).size() >= 2; + } + + void get_object_transforms(Object &object, + Span<float> simulation_times, + MutableSpan<float4x4> r_transforms) const + { + assert_same_size(simulation_times, r_transforms); + Span<float4x4> cached_transforms = object_transforms_cache_.lookup(&object); + if (cached_transforms.size() == 0) { + r_transforms.fill(object.obmat); + return; + } + if (cached_transforms.size() == 1) { + r_transforms.fill(cached_transforms[0]); + return; + } + + for (int i : simulation_times.index_range()) { + const float simulation_time = simulation_times[i]; + if (simulation_time <= simulation_time_interval_.start()) { + r_transforms[i] = cached_transforms.first(); + continue; + } + if (simulation_time >= simulation_time_interval_.stop()) { + r_transforms[i] = cached_transforms.last(); + continue; + } + const float factor = simulation_time_interval_.factor_at_time(simulation_time); + BLI_assert(factor > 0.0f && factor < 1.0f); + const float scaled_factor = factor * (cached_transforms.size() - 1); + const int lower_sample = (int)scaled_factor; + const int upper_sample = lower_sample + 1; + const float mix_factor = scaled_factor - lower_sample; + r_transforms[i] = float4x4::interpolate( + cached_transforms[lower_sample], cached_transforms[upper_sample], mix_factor); + } + } +}; + +static void sample_object_transforms(Object &object, + Depsgraph &depsgraph, + Scene &scene, + TimeInterval scene_frame_interval, + MutableSpan<float4x4> r_transforms) +{ + if (r_transforms.size() == 0) { + return; + } + if (r_transforms.size() == 1) { + r_transforms[0] = object.obmat; + return; + } + + Array<float> frames(r_transforms.size()); + scene_frame_interval.compute_uniform_samples(frames); + + for (int i : frames.index_range()) { + float frame = frames[i]; + const int recursion_depth = 5; + BKE_object_modifier_update_subframe( + &depsgraph, &scene, &object, false, recursion_depth, frame, eModifierType_None); + r_transforms[i] = object.obmat; + } +} + +template<typename T> static bool all_values_equal(Span<T> values) +{ + if (values.size() == 0) { + return true; + } + for (const T &value : values.drop_front(1)) { + if (value != values[0]) { + return false; + } + } + return true; +} + +static void prepare_dependency_animations(Depsgraph &depsgraph, + Scene &scene, + Simulation &simulation, + TimeInterval scene_frame_interval, + SampledDependencyAnimations &r_dependency_animations) +{ + LISTBASE_FOREACH (SimulationDependency *, dependency, &simulation.dependencies) { + ID *id_cow = DEG_get_evaluated_id(&depsgraph, dependency->id); + if (id_cow == nullptr) { + continue; + } + if (GS(id_cow->name) != ID_OB) { + continue; + } + Object &object_cow = *(Object *)id_cow; + constexpr int sample_count = 10; + Array<float4x4, sample_count> transforms(sample_count); + sample_object_transforms(object_cow, depsgraph, scene, scene_frame_interval, transforms); + + /* If all samples are the same, only store one. */ + Span<float4x4> transforms_to_use = (all_values_equal(transforms.as_span())) ? + transforms.as_span().take_front(1) : + transforms.as_span(); + + r_dependency_animations.add_object_transforms(object_cow, transforms_to_use); + } +} + void update_simulation_in_depsgraph(Depsgraph *depsgraph, Scene *scene_cow, Simulation *simulation_cow) @@ -108,13 +239,20 @@ void update_simulation_in_depsgraph(Depsgraph *depsgraph, SimulationInfluences influences; RequiredStates required_states; - /* TODO: Use simulation_cow, but need to add depsgraph relations before that. */ - collect_simulation_influences(*simulation_orig, resources, influences, required_states); + collect_simulation_influences(*simulation_cow, resources, influences, required_states); + + bke::PersistentDataHandleMap handle_map; + LISTBASE_FOREACH (SimulationDependency *, dependency, &simulation_orig->dependencies) { + ID *id_cow = DEG_get_evaluated_id(depsgraph, dependency->id); + if (id_cow != nullptr) { + handle_map.add(dependency->handle, *id_cow); + } + } if (current_frame == 1) { reinitialize_empty_simulation_states(simulation_orig, required_states); - initialize_simulation_states(*simulation_orig, *depsgraph, influences); + initialize_simulation_states(*simulation_orig, *depsgraph, influences, handle_map); simulation_orig->current_frame = 1; copy_states_to_cow(simulation_orig, simulation_cow); @@ -122,12 +260,97 @@ void update_simulation_in_depsgraph(Depsgraph *depsgraph, else if (current_frame == simulation_orig->current_frame + 1) { update_simulation_state_list(simulation_orig, required_states); - float time_step = 1.0f / 24.0f; - solve_simulation_time_step(*simulation_orig, *depsgraph, influences, time_step); + const float fps = scene_cow->r.frs_sec / scene_cow->r.frs_sec_base; + const float time_step = 1.0f / fps; + TimeInterval scene_frame_interval(current_frame - 1, 1); + TimeInterval simulation_time_interval(simulation_orig->current_simulation_time, time_step); + SampledDependencyAnimations dependency_animations{simulation_time_interval}; + prepare_dependency_animations( + *depsgraph, *scene_cow, *simulation_orig, scene_frame_interval, dependency_animations); + + solve_simulation_time_step( + *simulation_orig, *depsgraph, influences, handle_map, dependency_animations, time_step); simulation_orig->current_frame = current_frame; copy_states_to_cow(simulation_orig, simulation_cow); } } +/* Returns true when dependencies have changed. */ +bool update_simulation_dependencies(Simulation *simulation) +{ + nodes::NodeTreeDependencies dependencies = nodes::find_node_tree_dependencies( + *simulation->nodetree); + + ListBase *dependency_list = &simulation->dependencies; + + bool dependencies_changed = false; + + Map<ID *, SimulationDependency *> dependency_by_id; + Map<SimulationDependency *, int> old_flag_by_dependency; + Set<int> used_handles; + + /* Remove unused handle items and clear flags that are reinitialized later. */ + LISTBASE_FOREACH_MUTABLE (SimulationDependency *, dependency, dependency_list) { + if (dependencies.depends_on(dependency->id)) { + dependency_by_id.add_new(dependency->id, dependency); + used_handles.add_new(dependency->handle); + old_flag_by_dependency.add_new(dependency, dependency->flag); + dependency->flag &= ~(SIM_DEPENDS_ON_TRANSFORM | SIM_DEPENDS_ON_GEOMETRY); + } + else { + if (dependency->id != nullptr) { + id_us_min(dependency->id); + } + BLI_remlink(dependency_list, dependency); + MEM_freeN(dependency); + dependencies_changed = true; + } + } + + /* Add handle items for new id dependencies. */ + int next_handle = 0; + for (ID *id : dependencies.id_dependencies()) { + dependency_by_id.lookup_or_add_cb(id, [&]() { + while (used_handles.contains(next_handle)) { + next_handle++; + } + used_handles.add_new(next_handle); + + SimulationDependency *dependency = (SimulationDependency *)MEM_callocN(sizeof(*dependency), + AT); + id_us_plus(id); + dependency->id = id; + dependency->handle = next_handle; + BLI_addtail(dependency_list, dependency); + + return dependency; + }); + } + + /* Set appropriate dependency flags. */ + for (Object *object : dependencies.transform_dependencies()) { + SimulationDependency *dependency = dependency_by_id.lookup(&object->id); + dependency->flag |= SIM_DEPENDS_ON_TRANSFORM; + } + for (Object *object : dependencies.geometry_dependencies()) { + SimulationDependency *dependency = dependency_by_id.lookup(&object->id); + dependency->flag |= SIM_DEPENDS_ON_GEOMETRY; + } + + if (!dependencies_changed) { + /* Check if any flags have changed. */ + LISTBASE_FOREACH (SimulationDependency *, dependency, dependency_list) { + uint32_t old_flag = old_flag_by_dependency.lookup_default(dependency, 0); + uint32_t new_flag = dependency->flag; + if (old_flag != new_flag) { + dependencies_changed = true; + break; + } + } + } + + return dependencies_changed; +} + } // namespace blender::sim diff --git a/source/blender/simulation/intern/time_interval.hh b/source/blender/simulation/intern/time_interval.hh index 49600dd10de..5996dc5d25b 100644 --- a/source/blender/simulation/intern/time_interval.hh +++ b/source/blender/simulation/intern/time_interval.hh @@ -22,7 +22,7 @@ namespace blender::sim { /** - * The start time is inclusive and the end time is exclusive. The duration is zero, the interval + * The start time is exclusive and the end time is inclusive. If the duration is zero, the interval * describes a single point in time. */ class TimeInterval { @@ -41,7 +41,7 @@ class TimeInterval { return start_; } - float end() const + float stop() const { return start_ + duration_; } @@ -50,6 +50,42 @@ class TimeInterval { { return duration_; } + + float time_at_factor(float factor) const + { + return start_ + factor * duration_; + } + + float factor_at_time(float time) const + { + BLI_assert(duration_ > 0.0f); + return (time - start_) / duration_; + } + + float safe_factor_at_time(float time) const + { + if (duration_ > 0.0f) { + return this->factor_at_time(time); + } + return 0.0f; + } + + void compute_uniform_samples(MutableSpan<float> r_samples) const + { + int64_t amount = r_samples.size(); + if (amount == 0) { + return; + } + if (amount == 1) { + r_samples[0] = this->time_at_factor(0.5f); + return; + } + + const float step = duration_ / (float)(amount - 1); + for (int64_t i : r_samples.index_range()) { + r_samples[i] = start_ + i * step; + } + } }; } // namespace blender::sim diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo.c index e687af15982..90d9ba45637 100644 --- a/source/blender/windowmanager/gizmo/intern/wm_gizmo.c +++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo.c @@ -29,7 +29,6 @@ #include "BKE_context.h" #include "GPU_batch.h" -#include "GPU_glew.h" #include "RNA_access.h" #include "RNA_define.h" diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c index 396b59ba6e2..2038a82c9fd 100644 --- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c +++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c @@ -38,7 +38,6 @@ #include "ED_select_utils.h" #include "ED_view3d.h" -#include "GPU_glew.h" #include "GPU_matrix.h" #include "GPU_select.h" #include "GPU_state.h" @@ -582,7 +581,7 @@ static int gizmo_find_intersected_3d_intern(wmGizmo **visible_gizmos, Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); rcti rect; /* Almost certainly overkill, but allow for many custom gizmos. */ - GLuint buffer[MAXPICKBUF]; + uint buffer[MAXPICKBUF]; short hits; BLI_rcti_init_pt_radius(&rect, co, hotspot); @@ -625,7 +624,7 @@ static int gizmo_find_intersected_3d_intern(wmGizmo **visible_gizmos, GPU_matrix_unproject_with_precalc(&unproj_precalc, co_screen, co_3d_origin); - GLuint *buf_iter = buffer; + uint *buf_iter = buffer; int hit_found = -1; float dot_best = FLT_MAX; @@ -649,7 +648,7 @@ static int gizmo_find_intersected_3d_intern(wmGizmo **visible_gizmos, return hit_found; } else { - const GLuint *hit_near = GPU_select_buffer_near(buffer, hits); + const uint *hit_near = GPU_select_buffer_near(buffer, hits); return hit_near ? hit_near[3] : -1; } } diff --git a/source/blender/windowmanager/intern/wm_dragdrop.c b/source/blender/windowmanager/intern/wm_dragdrop.c index ad3fc7a1302..5f13adcb971 100644 --- a/source/blender/windowmanager/intern/wm_dragdrop.c +++ b/source/blender/windowmanager/intern/wm_dragdrop.c @@ -39,7 +39,6 @@ #include "BKE_context.h" #include "BKE_idtype.h" -#include "GPU_glew.h" #include "GPU_shader.h" #include "GPU_state.h" #include "GPU_viewport.h" @@ -424,9 +423,8 @@ void wm_drags_draw(bContext *C, wmWindow *win, rcti *rect) y, drag->imb->x, drag->imb->y, - GL_RGBA, - GL_UNSIGNED_BYTE, - GL_NEAREST, + GPU_RGBA8, + false, drag->imb->rect, drag->scale, drag->scale, diff --git a/source/blender/windowmanager/intern/wm_gesture.c b/source/blender/windowmanager/intern/wm_gesture.c index eee93fc9459..5a66cbd10eb 100644 --- a/source/blender/windowmanager/intern/wm_gesture.c +++ b/source/blender/windowmanager/intern/wm_gesture.c @@ -362,18 +362,8 @@ static void draw_filled_lasso(wmGesture *gt) GPU_shader_uniform_vector( state.shader, GPU_shader_get_uniform(state.shader, "shuffle"), 4, 1, red); - immDrawPixelsTex(&state, - rect.xmin, - rect.ymin, - w, - h, - GL_RED, - GL_UNSIGNED_BYTE, - GL_NEAREST, - pixel_buf, - 1.0f, - 1.0f, - NULL); + immDrawPixelsTex( + &state, rect.xmin, rect.ymin, w, h, GL_R8, false, pixel_buf, 1.0f, 1.0f, NULL); GPU_shader_unbind(); diff --git a/source/blender/windowmanager/intern/wm_playanim.c b/source/blender/windowmanager/intern/wm_playanim.c index 74e1c495a00..d0a70596957 100644 --- a/source/blender/windowmanager/intern/wm_playanim.c +++ b/source/blender/windowmanager/intern/wm_playanim.c @@ -335,9 +335,8 @@ static void playanim_toscreen( offs_y + (ps->draw_flip[1] ? span_y : 0.0f), ibuf->x, ibuf->y, - GL_RGBA, - GL_UNSIGNED_BYTE, - GL_NEAREST, + GPU_RGBA8, + false, ibuf->rect, ((ps->draw_flip[0] ? -1.0f : 1.0f)) * (ps->zoom / (float)ps->win_x), ((ps->draw_flip[1] ? -1.0f : 1.0f)) * (ps->zoom / (float)ps->win_y), diff --git a/source/blender/windowmanager/intern/wm_subwindow.c b/source/blender/windowmanager/intern/wm_subwindow.c index 477579ed620..0cb76404258 100644 --- a/source/blender/windowmanager/intern/wm_subwindow.c +++ b/source/blender/windowmanager/intern/wm_subwindow.c @@ -29,7 +29,6 @@ #include "DNA_screen_types.h" #include "DNA_windowmanager_types.h" -#include "GPU_glew.h" #include "GPU_matrix.h" #include "GPU_viewport.h" diff --git a/source/blender/windowmanager/wm_draw.h b/source/blender/windowmanager/wm_draw.h index ff2fc25333a..32c66a257e9 100644 --- a/source/blender/windowmanager/wm_draw.h +++ b/source/blender/windowmanager/wm_draw.h @@ -24,8 +24,6 @@ #ifndef __WM_DRAW_H__ #define __WM_DRAW_H__ -#include "GPU_glew.h" - struct GPUOffScreen; struct GPUTexture; struct GPUViewport; diff --git a/tests/gtests/CMakeLists.txt b/tests/gtests/CMakeLists.txt index 282eb9080f5..9a7509f81f3 100644 --- a/tests/gtests/CMakeLists.txt +++ b/tests/gtests/CMakeLists.txt @@ -12,7 +12,6 @@ if(WITH_GTESTS) add_subdirectory(blenloader) add_subdirectory(guardedalloc) add_subdirectory(bmesh) - add_subdirectory(functions) if(WITH_CODEC_FFMPEG) add_subdirectory(ffmpeg) endif() diff --git a/tests/gtests/blenlib/CMakeLists.txt b/tests/gtests/blenlib/CMakeLists.txt index d151dacd7a4..0727d317eaa 100644 --- a/tests/gtests/blenlib/CMakeLists.txt +++ b/tests/gtests/blenlib/CMakeLists.txt @@ -39,46 +39,31 @@ else() set(BLI_path_util_extra_libs "bf_blenlib;extern_wcwidth;${ZLIB_LIBRARIES}") endif() -BLENDER_TEST(BLI_array "bf_blenlib") BLENDER_TEST(BLI_array_store "bf_blenlib") BLENDER_TEST(BLI_array_utils "bf_blenlib") BLENDER_TEST(BLI_delaunay_2d "bf_blenlib") -BLENDER_TEST(BLI_disjoint_set "bf_blenlib") -BLENDER_TEST(BLI_edgehash "bf_blenlib") BLENDER_TEST(BLI_expr_pylike_eval "bf_blenlib") BLENDER_TEST(BLI_ghash "bf_blenlib") BLENDER_TEST(BLI_hash_mm2a "bf_blenlib") BLENDER_TEST(BLI_heap "bf_blenlib") BLENDER_TEST(BLI_heap_simple "bf_blenlib") -BLENDER_TEST(BLI_index_mask "bf_blenlib") -BLENDER_TEST(BLI_index_range "bf_blenlib") BLENDER_TEST(BLI_kdopbvh "bf_blenlib;bf_intern_numaapi") -BLENDER_TEST(BLI_linear_allocator "bf_blenlib") BLENDER_TEST(BLI_linklist_lockfree "bf_blenlib;bf_intern_numaapi") BLENDER_TEST(BLI_listbase "bf_blenlib") -BLENDER_TEST(BLI_map "bf_blenlib") BLENDER_TEST(BLI_math_base "bf_blenlib") -BLENDER_TEST(BLI_math_base_safe "bf_blenlib") BLENDER_TEST(BLI_math_bits "bf_blenlib") BLENDER_TEST(BLI_math_color "bf_blenlib") BLENDER_TEST(BLI_math_geom "bf_blenlib") BLENDER_TEST(BLI_math_matrix "bf_blenlib") BLENDER_TEST(BLI_math_vector "bf_blenlib") BLENDER_TEST(BLI_memiter "bf_blenlib") -BLENDER_TEST(BLI_memory_utils "bf_blenlib") BLENDER_TEST(BLI_path_util "${BLI_path_util_extra_libs}") BLENDER_TEST(BLI_polyfill_2d "bf_blenlib") -BLENDER_TEST(BLI_set "bf_blenlib") -BLENDER_TEST(BLI_span "bf_blenlib") BLENDER_TEST(BLI_stack "bf_blenlib") -BLENDER_TEST(BLI_stack_cxx "bf_blenlib") BLENDER_TEST(BLI_string "bf_blenlib") -BLENDER_TEST(BLI_string_ref "bf_blenlib") BLENDER_TEST(BLI_string_utf8 "bf_blenlib") BLENDER_TEST(BLI_task "bf_blenlib;bf_intern_numaapi") BLENDER_TEST(BLI_task_graph "bf_blenlib;bf_intern_numaapi") -BLENDER_TEST(BLI_vector "bf_blenlib") -BLENDER_TEST(BLI_vector_set "bf_blenlib") BLENDER_TEST_PERFORMANCE(BLI_ghash_performance "bf_blenlib") BLENDER_TEST_PERFORMANCE(BLI_task_performance "bf_blenlib") diff --git a/tests/gtests/functions/CMakeLists.txt b/tests/gtests/functions/CMakeLists.txt deleted file mode 100644 index 1246bbd5599..00000000000 --- a/tests/gtests/functions/CMakeLists.txt +++ /dev/null @@ -1,45 +0,0 @@ -# ***** BEGIN GPL LICENSE BLOCK ***** -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# ***** END GPL LICENSE BLOCK ***** - -set(INC - . - .. - ../../../source/blender/blenlib - ../../../source/blender/functions - ../../../source/blender/makesdna - ../../../intern/guardedalloc -) - -setup_libdirs() -include_directories(${INC}) - - -set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${PLATFORM_LINKFLAGS}") -set(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} ${PLATFORM_LINKFLAGS_DEBUG}") - -if(WITH_BUILDINFO) - set(BUILDINFO buildinfoobj) -endif() - -BLENDER_TEST(FN_array_spans "bf_blenlib;bf_functions;${BUILDINFO}") -BLENDER_TEST(FN_attributes_ref "bf_blenlib;bf_functions;${BUILDINFO}") -BLENDER_TEST(FN_cpp_type "bf_blenlib;bf_functions;${BUILDINFO}") -BLENDER_TEST(FN_generic_vector_array "bf_blenlib;bf_functions;${BUILDINFO}") -BLENDER_TEST(FN_multi_function "bf_blenlib;bf_functions;${BUILDINFO}") -BLENDER_TEST(FN_multi_function_network "bf_blenlib;bf_functions;${BUILDINFO}") -BLENDER_TEST(FN_spans "bf_blenlib;bf_functions;${BUILDINFO}") diff --git a/tests/gtests/runner/CMakeLists.txt b/tests/gtests/runner/CMakeLists.txt index 18d8f57364f..59537aab571 100644 --- a/tests/gtests/runner/CMakeLists.txt +++ b/tests/gtests/runner/CMakeLists.txt @@ -36,7 +36,7 @@ endif() # directly referenced from other code. get_property(_test_libs GLOBAL PROPERTY BLENDER_TEST_LIBS) if(WIN32) - list(APPEND TEST_LIBS ${_test_libs}) + # Win32 is set using target_link_options after target creation. elseif(APPLE) list(APPEND TEST_LIBS "-Wl,-force_load" ${_test_libs}) elseif(UNIX) @@ -44,7 +44,6 @@ elseif(UNIX) else() message(FATAL_ERROR "Unknown how to link whole-archive with your compiler ${CMAKE_CXX_COMPILER_ID}") endif() -unset(_test_libs) # This builds `bin/tests/blender_test`, but does not add it as a single test. setup_libdirs() @@ -56,6 +55,18 @@ BLENDER_SRC_GTEST_EX( ) setup_liblinks(blender_test) +if(WIN32) + foreach(_lib ${_test_libs}) + # Both target_link_libraries and target_link_options are required here + # target_link_libraries will add any dependend libraries, while just setting + # the wholearchive flag in target link options will not. + target_link_libraries(blender_test ${_lib}) + target_link_options(blender_test PRIVATE /wholearchive:$<TARGET_FILE:${_lib}>) + endforeach() +endif() + +unset(_test_libs) + # This runs the blender_test executable with `--gtest_list_tests`, then # exposes those tests individually to the ctest runner. # See https://cmake.org/cmake/help/v3.18/module/GoogleTest.html @@ -66,7 +77,9 @@ set(_GOOGLETEST_DISCOVER_TESTS_SCRIPT ) gtest_discover_tests(blender_test - # So that unit tests know where to find files: +# So that the binary can find its shared libs on window. + WORKING_DIRECTORY "$<TARGET_FILE_DIR:blender>" +# So that unit tests know where to find files: EXTRA_ARGS --test-assets-dir "${CMAKE_SOURCE_DIR}/../lib/tests" --test-release-dir "${CMAKE_INSTALL_PREFIX}/${BLENDER_VERSION}" |