Removed mBodyNode from CharacterBase

This commit is contained in:
2026-02-25 14:51:26 +03:00
parent ef4c675d98
commit cb2ce23009
14 changed files with 1497 additions and 471 deletions

View File

@@ -229,7 +229,17 @@ function(weight_clothes SRC)
${CMAKE_CURRENT_BINARY_DIR}/blender-addons-installed
)
endfunction()
weight_clothes(${CMAKE_CURRENT_SOURCE_DIR}/clothes-male-bottom.blend)
function(transfer_shape_keys SRC CLOTH DST)
add_custom_command(
OUTPUT ${DST}
COMMAND ${BLENDER} -b -Y -P ${CMAKE_CURRENT_SOURCE_DIR}/transfer_shape_keys.py -- ${SRC} ${CLOTH} ${DST}
COMMAND ${CMAKE_COMMAND} -D FILE=${DST}
-P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/check_file_size.cmake
DEPENDS ${SRC} ${CLOTH}
${CMAKE_CURRENT_BINARY_DIR}/blender-addons-installed
${CMAKE_CURRENT_SOURCE_DIR}/transfer_shape_keys.py
)
endfunction()
# Function to combine clothes into a blend file
# Parameters:
@@ -383,15 +393,21 @@ function(add_clothes_pipeline INPUT_BLEND WEIGHTED_BLEND FINAL_OUTPUT_BLEND)
else()
set(COMBINED_BLEND "${PIPELINE_INTERMEDIATE_DIR}/${TARGET_BASE}_combined.blend")
endif()
set(SHAPED_BLEND "${PIPELINE_INTERMEDIATE_DIR}/${TARGET_BASE}_shaped.blend")
# Step 1: Combine clothes
add_clothes_combination(
"${INPUT_BLEND}"
"${WEIGHTED_BLEND}"
"${WEIGHTED_BLEND}"
"${COMBINED_BLEND}"
OUTPUT_DIR "${PIPELINE_INTERMEDIATE_DIR}"
)
transfer_shape_keys(${INPUT_BLEND}
${COMBINED_BLEND}
${SHAPED_BLEND}
)
# Step 2: Consolidate
add_blend_consolidation(
"${INPUT_BLEND}"
@@ -401,11 +417,13 @@ function(add_clothes_pipeline INPUT_BLEND WEIGHTED_BLEND FINAL_OUTPUT_BLEND)
# Create a custom target to drive the whole pipeline
add_custom_target(${TARGET_BASE}_pipeline ALL
DEPENDS ${FINAL_OUTPUT_BLEND}
DEPENDS ${FINAL_OUTPUT_BLEND} ${SHAPED_BLEND}
COMMENT "Running complete clothes pipeline for ${TARGET_BASE}"
)
endfunction()
weight_clothes(${CMAKE_CURRENT_SOURCE_DIR}/clothes-male-bottom.blend)
add_clothes_pipeline(
"${CMAKE_CURRENT_SOURCE_DIR}/edited-normal-male.blend" # INPUT_BLEND
"${CMAKE_CURRENT_BINARY_DIR}/clothes/clothes-male-bottom_weighted.blend" # WEIGHTED_BLEND

View File

@@ -0,0 +1,970 @@
"""
Blender 3.6.20 Script: Transfer Shape Keys with Boundary Velocity Limiting
Usage: blender -b --python transfer_shape_keys.py -- <source_file> <target_file> <output_file>
"""
import bpy
import sys
import os
import shutil
import mathutils
from mathutils.bvhtree import BVHTree
import numpy as np
from collections import defaultdict
def parse_args():
"""Parse command line arguments after '--'"""
if '--' in sys.argv:
args_start = sys.argv.index('--') + 1
args = sys.argv[args_start:]
if len(args) >= 3:
return args[0], args[1], args[2]
else:
print("Error: Please provide source, target, and output .blend files")
print("Usage: blender -b --python transfer_shape_keys.py -- <source_file> <target_file> <output_file>")
sys.exit(1)
else:
print("Error: No arguments provided")
print("Usage: blender -b --python transfer_shape_keys.py -- <source_file> <target_file> <output_file>")
sys.exit(1)
def load_source_data(source_path):
"""Load source file and extract shape key information"""
print(f"\nLoading source file: {source_path}")
if not os.path.exists(source_path):
print(f"Error: Source file not found: {source_path}")
sys.exit(1)
current_file = bpy.data.filepath if bpy.data.filepath else None
bpy.ops.wm.open_mainfile(filepath=source_path)
body_object = None
for obj in bpy.data.objects:
if obj.name == "Body_shapes" and obj.type == 'MESH':
body_object = obj
break
if not body_object:
for obj in bpy.data.objects:
if obj.name == "Body" and obj.type == 'MESH':
body_object = obj
break
if not body_object:
print("Error: Could not find mesh object named 'Body' in source file")
sys.exit(1)
if not body_object.data.shape_keys:
print("Error: Body object has no shape keys")
sys.exit(1)
shape_key_data = {
'names': [],
'vertex_positions': {},
'polygons': [],
'is_relative': body_object.data.shape_keys.use_relative
}
# Store polygon data for BVH
mesh = body_object.data
for poly in mesh.polygons:
shape_key_data['polygons'].append([v for v in poly.vertices])
source_shape_keys = body_object.data.shape_keys.key_blocks
print(f"Found Body object with {len(source_shape_keys)} shape keys")
for sk in source_shape_keys:
shape_key_data['names'].append(sk.name)
print(f" - {sk.name}")
vertex_positions = []
for v in sk.data:
vertex_positions.append((v.co.x, v.co.y, v.co.z))
shape_key_data['vertex_positions'][sk.name] = vertex_positions
if current_file and os.path.exists(current_file):
bpy.ops.wm.open_mainfile(filepath=current_file)
elif current_file:
bpy.ops.wm.read_homefile()
return shape_key_data
def load_target_file(target_path):
"""Load the target .blend file and find objects with custom properties"""
print(f"\nLoading target file: {target_path}")
if not os.path.exists(target_path):
print(f"Error: Target file not found: {target_path}")
sys.exit(1)
temp_dir = os.path.join(os.path.dirname(target_path), "temp_blend_files")
os.makedirs(temp_dir, exist_ok=True)
temp_target = os.path.join(temp_dir, os.path.basename(target_path))
shutil.copy2(target_path, temp_target)
bpy.ops.wm.open_mainfile(filepath=temp_target)
target_objects = []
for obj in bpy.data.objects:
if obj.type == 'MESH' and obj.data:
if all(prop in obj for prop in ['age', 'sex', 'slot']):
target_objects.append(obj)
print(f"Found target object: {obj.name}")
print(f" - age: {obj['age']}")
print(f" - sex: {obj['sex']}")
print(f" - slot: {obj['slot']}")
print(f" - vertices: {len(obj.data.vertices)}")
return target_objects, temp_target
def delete_existing_shape_keys(target_obj):
"""Delete all existing shape keys from target object"""
if not target_obj.data.shape_keys:
return False
num_keys = len(target_obj.data.shape_keys.key_blocks)
print(f" Deleting {num_keys} existing shape keys...")
bpy.context.view_layer.objects.active = target_obj
target_obj.select_set(True)
bpy.ops.object.mode_set(mode='OBJECT')
while target_obj.data.shape_keys:
target_obj.active_shape_key_index = 0
bpy.ops.object.shape_key_remove()
target_obj.select_set(False)
return True
def ensure_shape_keys_structure(shape_key_names, target_obj):
"""Create shape key structure on target object"""
delete_existing_shape_keys(target_obj)
bpy.context.view_layer.objects.active = target_obj
target_obj.select_set(True)
# Create Basis with current mesh positions
target_obj.shape_key_add(name='Basis', from_mix=True)
print(f" Created Basis shape key")
# Create other shape keys
for sk_name in shape_key_names:
if sk_name != 'Basis':
target_obj.shape_key_add(name=sk_name, from_mix=False)
print(f" Created shape key: {sk_name}")
target_obj.data.shape_keys.use_relative = True
target_obj.select_set(False)
def create_bvh_for_source(source_data, shape_key_name='Basis'):
"""Create BVH tree for source mesh in specified pose"""
positions = source_data['vertex_positions'][shape_key_name]
verts = [mathutils.Vector(v) for v in positions]
# Create BVH tree
bvh = BVHTree.FromPolygons(verts, source_data['polygons'], all_triangles=False)
return bvh, verts
def compute_signed_distance_and_direction(bvh, point, reference_normal=None):
"""Compute signed distance and direction to surface"""
# Find closest point on surface
location, normal, index, distance = bvh.find_nearest(point)
if location is None:
return None, None, None, None
# Determine sign using reference normal
if reference_normal is not None:
to_surface = location - point
if to_surface.length > 0:
dot = to_surface.normalized().dot(reference_normal)
signed_distance = distance if dot > 0 else -distance
else:
signed_distance = 0
else:
to_surface = point - location
if to_surface.length > 0 and normal.length > 0:
dot = to_surface.normalized().dot(normal)
signed_distance = distance if dot > 0 else -distance
else:
signed_distance = distance
return location, normal, index, signed_distance
def detect_boundary_vertices(target_obj, mapping, threshold=0.3):
"""Detect vertices that lie on the boundary between inner and outer surfaces"""
print(f" Detecting boundary vertices...")
num_verts = len(mapping['target_verts'])
adjacency = mapping['adjacency']
boundary_vertices = set()
# For each vertex, check if its neighbors have different side flags
for i in range(num_verts):
if i >= len(mapping['side_flags']):
continue
side_i = mapping['side_flags'][i]
if side_i == 0: # On surface, definitely boundary
boundary_vertices.add(i)
continue
neighbors = adjacency[i]
opposite_side_count = 0
total_neighbors = 0
for n in neighbors:
if n < len(mapping['side_flags']):
side_n = mapping['side_flags'][n]
if side_n != 0 and side_n != side_i:
opposite_side_count += 1
total_neighbors += 1
# If significant portion of neighbors are on opposite side, this is a boundary
if total_neighbors > 0 and opposite_side_count / total_neighbors > threshold:
boundary_vertices.add(i)
# Also check vertices that are close to the surface (small signed distance)
for i in range(num_verts):
if i < len(mapping['signed_distances']):
if abs(mapping['signed_distances'][i]) < 0.05: # Very close to surface
boundary_vertices.add(i)
print(f" Found {len(boundary_vertices)} boundary vertices")
return boundary_vertices
def compute_robust_surface_mapping(target_obj, source_data):
"""Create robust surface mapping with signed distances"""
print(f" Computing robust surface mapping...")
# Get target vertices and normals
target_verts = [v.co.copy() for v in target_obj.data.vertices]
target_normals = [v.normal.copy() for v in target_obj.data.vertices]
num_verts = len(target_verts)
# Create BVH for source basis
bvh_basis, basis_verts = create_bvh_for_source(source_data, 'Basis')
# Build adjacency for later smoothing
adjacency = defaultdict(set)
mesh = target_obj.data
for edge in mesh.edges:
v1, v2 = edge.vertices
adjacency[v1].add(v2)
adjacency[v2].add(v1)
mapping = {
'target_verts': target_verts,
'source_indices': [],
'surface_points': [],
'signed_distances': [],
'direction_vectors': [],
'face_indices': [],
'barycentric_coords': [],
'side_flags': [],
'confidence': [],
'adjacency': adjacency,
'deformation_magnitude': [0.0] * num_verts,
'boundary_vertices': set()
}
# First pass: find closest points and determine side
for i, (target_pos, target_normal) in enumerate(zip(target_verts, target_normals)):
location, normal, index, signed_dist = compute_signed_distance_and_direction(
bvh_basis, target_pos, target_normal
)
if location is None:
# Fallback to nearest vertex
if i % 100 == 0:
print(f" Warning: Vertex {i} using nearest vertex fallback")
min_dist = float('inf')
nearest_idx = 0
for j, src_pos in enumerate(basis_verts):
dist = (target_pos - src_pos).length
if dist < min_dist:
min_dist = dist
nearest_idx = j
mapping['source_indices'].append([nearest_idx])
mapping['surface_points'].append(basis_verts[nearest_idx])
mapping['signed_distances'].append(min_dist)
mapping['direction_vectors'].append(target_pos - basis_verts[nearest_idx])
mapping['face_indices'].append(-1)
mapping['barycentric_coords'].append([1.0])
mapping['side_flags'].append(1)
mapping['confidence'].append(0.5)
else:
mapping['surface_points'].append(location)
mapping['signed_distances'].append(signed_dist)
abs_dist = abs(signed_dist)
if abs_dist < 0.001:
side_flag = 0
confidence = 1.0
else:
ray_hits = 0
for ray_dir in [target_normal, -target_normal, mathutils.Vector((1,0,0)), mathutils.Vector((-1,0,0))]:
hit, _, _, _ = bvh_basis.ray_cast(target_pos + ray_dir * 0.1, -ray_dir)
if hit is not None:
ray_hits += 1
side_flag = 1 if signed_dist > 0 else -1
confidence = min(1.0, ray_hits / 4.0 + 0.5)
mapping['side_flags'].append(side_flag)
mapping['confidence'].append(confidence)
if index is not None and index < len(source_data['polygons']):
face = source_data['polygons'][index]
mapping['face_indices'].append(index)
if len(face) == 3:
face_verts = [basis_verts[idx] for idx in face]
try:
coords = mathutils.geometry.barycentric_transform(
location, face_verts[0], face_verts[1], face_verts[2]
)
mapping['barycentric_coords'].append(coords)
min_dist = float('inf')
nearest_idx = face[0]
for idx in face:
dist = (location - basis_verts[idx]).length
if dist < min_dist:
min_dist = dist
nearest_idx = idx
mapping['source_indices'].append([nearest_idx])
except:
min_dist = float('inf')
nearest_idx = face[0]
for idx in face:
dist = (location - basis_verts[idx]).length
if dist < min_dist:
min_dist = dist
nearest_idx = idx
mapping['source_indices'].append([nearest_idx])
mapping['barycentric_coords'].append([1.0])
else:
min_dist = float('inf')
nearest_idx = face[0]
for idx in face:
dist = (location - basis_verts[idx]).length
if dist < min_dist:
min_dist = dist
nearest_idx = idx
mapping['source_indices'].append([nearest_idx])
mapping['barycentric_coords'].append([1.0])
else:
mapping['face_indices'].append(-1)
mapping['source_indices'].append([0])
mapping['barycentric_coords'].append([1.0])
direction = target_pos - location
mapping['direction_vectors'].append(direction)
# Detect boundary vertices
mapping['boundary_vertices'] = detect_boundary_vertices(target_obj, mapping)
print(f" Mapping complete. Side distribution: "
f"Outside: {mapping['side_flags'].count(1)}, "
f"Inside: {mapping['side_flags'].count(-1)}, "
f"On surface: {mapping['side_flags'].count(0)}")
return mapping
def interpolate_source_position_safe(source_data, sk_name, surface_point, face_idx, bary_coords, source_indices):
"""Interpolate source position with side preservation"""
if sk_name == 'Basis':
return surface_point
source_positions = source_data['vertex_positions'][sk_name]
if face_idx >= 0 and face_idx < len(source_data['polygons']):
face = source_data['polygons'][face_idx]
if len(face) == 3 and len(bary_coords) == 3:
pos = mathutils.Vector((0, 0, 0))
for j, vert_idx in enumerate(face):
if j < len(bary_coords) and vert_idx < len(source_positions):
pos += bary_coords[j] * mathutils.Vector(source_positions[vert_idx])
return pos
if source_indices and len(source_indices) > 0:
idx = source_indices[0]
if idx < len(source_positions):
return mathutils.Vector(source_positions[idx])
return surface_point
def compute_deformation_magnitudes(source_data, mapping):
"""Compute how much each vertex deforms in the source"""
print(f" Computing deformation magnitudes...")
basis_positions = [mathutils.Vector(v) for v in source_data['vertex_positions']['Basis']]
num_verts = len(mapping['target_verts'])
magnitudes = [0.0] * num_verts
for sk_name in source_data['names']:
if sk_name == 'Basis':
continue
sk_positions = [mathutils.Vector(v) for v in source_data['vertex_positions'][sk_name]]
for i in range(num_verts):
if i < len(mapping['source_indices']):
src_indices = mapping['source_indices'][i]
if src_indices and len(src_indices) > 0:
src_idx = src_indices[0]
if src_idx < len(basis_positions) and src_idx < len(sk_positions):
deformation = (sk_positions[src_idx] - basis_positions[src_idx]).length
magnitudes[i] = max(magnitudes[i], deformation)
max_mag = max(magnitudes) if magnitudes else 1.0
if max_mag > 0:
magnitudes = [m / max_mag for m in magnitudes]
high_count = sum(1 for m in magnitudes if m > 0.7)
print(f" High deformation vertices (>0.7): {high_count}/{num_verts}")
return magnitudes
def enforce_side_constraint(pos, surface_point, reference_normal, target_side, confidence, is_boundary=False):
"""Enforce that vertex stays on correct side of surface"""
to_surface = surface_point - pos
if to_surface.length < 0.001:
return pos
current_side = 1 if to_surface.dot(reference_normal) < 0 else -1
# For boundary vertices, use very gentle enforcement
if is_boundary:
if current_side != target_side and target_side != 0:
# Just pull back slightly toward surface
proj_factor = to_surface.dot(reference_normal) / reference_normal.length_squared
proj_point = pos + reference_normal * proj_factor
# Very small offset to stay near surface
offset = abs((surface_point - pos).length) * 0.2
if target_side > 0:
return proj_point - reference_normal * offset
else:
return proj_point + reference_normal * offset
return pos
# Regular vertices - stronger enforcement
if current_side != target_side and target_side != 0:
proj_factor = to_surface.dot(reference_normal) / reference_normal.length_squared
proj_point = pos + reference_normal * proj_factor
offset = abs((surface_point - pos).length) * 0.95
if target_side > 0:
corrected_pos = proj_point - reference_normal * offset
else:
corrected_pos = proj_point + reference_normal * offset
return corrected_pos
return pos
def limit_boundary_velocity(target_idx, target_pos, deformed_surface, t, mapping):
"""Limit how fast boundary vertices can move"""
if target_idx not in mapping['boundary_vertices']:
return deformed_surface
# Get the full deformation target
full_target = deformed_surface
# Calculate how far this vertex should move at this t value
# Use a slower progression for boundary vertices
safe_t = t * 0.7 # Boundary vertices only move 70% as fast
# Interpolate between start and target with limited speed
limited_pos = (1 - safe_t) * target_pos + safe_t * full_target
return limited_pos
def adaptive_damping(pos, target_pos, deformed_surface, magnitude, t, threshold=0.55, is_boundary=False):
"""Apply adaptive damping based on deformation magnitude and t value"""
if t <= threshold:
return pos
# For boundary vertices, use much less damping to avoid pinching
if is_boundary:
return pos
excess = (t - threshold) / (1.0 - threshold)
damping = min(1.0, magnitude * 0.8 + 0.2)
damped_weight = excess * damping * 0.5
safe_pos = (target_pos + deformed_surface) * 0.5
damped_pos = (1 - damped_weight) * pos + damped_weight * safe_pos
return damped_pos
def compute_side_preserving_position(target_idx, sk_name, mapping, source_data, t):
"""Compute position that preserves side and signed distance"""
if target_idx >= len(mapping['target_verts']):
return mathutils.Vector((0, 0, 0))
target_pos = mapping['target_verts'][target_idx]
if target_idx >= len(mapping['surface_points']):
return target_pos
# Check if this is a boundary vertex
is_boundary = target_idx in mapping.get('boundary_vertices', set())
surface_point = mapping['surface_points'][target_idx]
signed_dist = mapping['signed_distances'][target_idx] if target_idx < len(mapping['signed_distances']) else 0
side_flag = mapping['side_flags'][target_idx] if target_idx < len(mapping['side_flags']) else 1
confidence = mapping['confidence'][target_idx] if target_idx < len(mapping['confidence']) else 0.5
face_idx = mapping['face_indices'][target_idx] if target_idx < len(mapping['face_indices']) else -1
bary_coords = mapping['barycentric_coords'][target_idx] if target_idx < len(mapping['barycentric_coords']) else [1.0]
source_indices = mapping['source_indices'][target_idx] if target_idx < len(mapping['source_indices']) else [0]
deformed_surface = interpolate_source_position_safe(
source_data, sk_name, surface_point, face_idx, bary_coords, source_indices
)
# For boundary vertices, limit their velocity
if is_boundary:
deformed_surface = limit_boundary_velocity(target_idx, target_pos, deformed_surface, t, mapping)
basis_surface = surface_point
current_surface = (1 - t) * basis_surface + t * deformed_surface
if target_idx < len(mapping['direction_vectors']):
reference_normal = mapping['direction_vectors'][target_idx].normalized()
else:
reference_normal = mathutils.Vector((0, 0, 1))
if reference_normal.length < 0.1:
reference_normal = mathutils.Vector((0, 0, 1))
abs_dist = abs(signed_dist)
# For boundary vertices, use a much smaller offset to keep them near the surface
if is_boundary:
# Boundary vertices should stay very close to the surface
# Use a fraction of their original distance
abs_dist = abs_dist * 0.2 # Only 20% of original offset
else:
# Regular vertices use full offset but with confidence weighting
abs_dist = abs_dist * confidence
if side_flag > 0:
base_pos = current_surface + reference_normal * abs_dist
elif side_flag < 0:
base_pos = current_surface - reference_normal * abs_dist
else:
base_pos = current_surface
direct_mapped = (1 - t) * target_pos + t * deformed_surface
# For boundary vertices, blend more heavily with direct mapping
if is_boundary:
blend_weight = 0.1 # Only 10% side-preserving for boundaries
else:
blend_weight = confidence * (1 - t * 0.3)
blended_pos = (1 - blend_weight) * direct_mapped + blend_weight * base_pos
if side_flag != 0:
final_pos = enforce_side_constraint(
blended_pos, current_surface, reference_normal, side_flag, confidence, is_boundary
)
else:
final_pos = blended_pos
if 'deformation_magnitude' in mapping:
if target_idx < len(mapping['deformation_magnitude']):
magnitude = mapping['deformation_magnitude'][target_idx]
final_pos = adaptive_damping(final_pos, target_pos, deformed_surface, magnitude, t, is_boundary=is_boundary)
return final_pos
def smooth_boundary_areas(target_obj, sk_name, mapping, source_data):
"""Specifically smooth boundary vertices to prevent them from popping out"""
print(f" Smoothing boundary areas for {sk_name}...")
sk = target_obj.data.shape_keys.key_blocks[sk_name]
current_positions = [v.co.copy() for v in sk.data]
num_verts = len(current_positions)
boundary_vertices = mapping.get('boundary_vertices', set())
if not boundary_vertices:
return
adjacency = mapping['adjacency']
# Multiple smoothing passes focused on boundaries
for iteration in range(3): # More iterations for boundaries
new_positions = current_positions.copy()
for i in boundary_vertices:
if i >= num_verts:
continue
neighbors = adjacency[i]
if not neighbors:
continue
# Average with neighbors, weighting by side similarity
weighted_sum = mathutils.Vector((0, 0, 0))
total_weight = 0
for n in neighbors:
if n < num_verts:
# Higher weight for neighbors on same side
if n < len(mapping['side_flags']) and i < len(mapping['side_flags']):
if mapping['side_flags'][n] == mapping['side_flags'][i]:
weight = 2.0
else:
weight = 1.0
else:
weight = 1.0
weighted_sum += current_positions[n] * weight
total_weight += weight
if total_weight > 0:
avg_pos = weighted_sum / total_weight
# Progressive smoothing
blend = 0.2 + iteration * 0.1
new_positions[i] = (1 - blend) * current_positions[i] + blend * avg_pos
current_positions = new_positions
# Apply smoothed positions
for i, pos in enumerate(current_positions):
if i < len(sk.data):
sk.data[i].co = pos
sk.data.update()
target_obj.data.update_tag()
bpy.context.view_layer.update()
print(f" Boundary smoothing complete")
def smooth_penetration_areas(target_obj, sk_name, mapping, source_data, threshold_t=0.55):
"""Smooth areas where penetration occurs at high t values"""
print(f" Smoothing penetration areas for {sk_name}...")
sk = target_obj.data.shape_keys.key_blocks[sk_name]
current_positions = [v.co.copy() for v in sk.data]
num_verts = len(current_positions)
boundary_vertices = mapping.get('boundary_vertices', set())
# Detect penetration at t=1.0
problem_vertices = set()
bvh_deformed, _ = create_bvh_for_source(source_data, sk_name)
for i, pos in enumerate(current_positions):
if i < len(mapping['surface_points']) and i < len(mapping['side_flags']):
surface_point = mapping['surface_points'][i]
target_side = mapping['side_flags'][i]
if target_side != 0:
location, _, _, _ = bvh_deformed.find_nearest(pos)
if location and i < len(mapping['direction_vectors']):
to_surface = pos - location
current_side = 1 if to_surface.dot(mapping['direction_vectors'][i]) > 0 else -1
if current_side != target_side:
problem_vertices.add(i)
if not problem_vertices:
print(f" No penetration detected")
return
print(f" Found {len(problem_vertices)} penetrating vertices")
# Separate boundary and interior problem vertices
boundary_problems = problem_vertices.intersection(boundary_vertices)
interior_problems = problem_vertices - boundary_vertices
if boundary_problems:
print(f" {len(boundary_problems)} are boundary vertices - these need special care")
# For boundary vertices, use very gentle smoothing
adjacency = mapping['adjacency']
for iteration in range(2):
new_positions = current_positions.copy()
for i in boundary_problems:
neighbors = adjacency[i]
if not neighbors:
continue
# Only average with non-problem neighbors
weighted_sum = mathutils.Vector((0, 0, 0))
total_weight = 0
for n in neighbors:
if n < num_verts and n not in boundary_problems:
weighted_sum += current_positions[n]
total_weight += 1
if total_weight > 0:
avg_pos = weighted_sum / total_weight
blend = 0.15 # Very gentle
new_positions[i] = (1 - blend) * current_positions[i] + blend * avg_pos
current_positions = new_positions
# Get vertices with large deformation
large_deformation = set()
if 'deformation_magnitude' in mapping:
for i, mag in enumerate(mapping['deformation_magnitude']):
if i < num_verts and mag > 0.7:
large_deformation.add(i)
# Focus on interior problems with large deformation
focus_vertices = interior_problems.intersection(large_deformation)
if focus_vertices:
print(f" {len(focus_vertices)} interior high-deformation vertices")
# Smooth interior problems
adjacency = mapping['adjacency']
for iteration in range(3):
new_positions = current_positions.copy()
for i in focus_vertices:
neighbors = adjacency[i]
if not neighbors:
continue
weighted_sum = mathutils.Vector((0, 0, 0))
total_weight = 0
for n in neighbors:
if n < num_verts:
weight = 1.0
if n in boundary_vertices:
weight = 0.3 # Less influence from boundaries
weighted_sum += current_positions[n] * weight
total_weight += weight
if total_weight > 0:
avg_pos = weighted_sum / total_weight
blend = 0.3 + iteration * 0.1
new_positions[i] = (1 - blend) * current_positions[i] + blend * avg_pos
current_positions = new_positions
# Apply smoothed positions
for i, pos in enumerate(current_positions):
if i < len(sk.data):
sk.data[i].co = pos
sk.data.update()
target_obj.data.update_tag()
bpy.context.view_layer.update()
print(f" Smoothing complete")
def set_shape_key_with_side_preservation(target_obj, sk_name, mapping, source_data):
"""Set shape key with side preservation"""
print(f" Setting {sk_name} with side preservation...")
if 'deformation_magnitude' not in mapping or len(mapping['deformation_magnitude']) != len(mapping['target_verts']):
mapping['deformation_magnitude'] = compute_deformation_magnitudes(source_data, mapping)
target_obj.data.shape_keys.use_relative = False
sk = target_obj.data.shape_keys.key_blocks[sk_name]
for i, v in enumerate(sk.data):
if i < len(mapping['target_verts']):
pos = compute_side_preserving_position(i, sk_name, mapping, source_data, 1.0)
v.co = pos
sk.data.update()
target_obj.data.update_tag()
bpy.context.view_layer.update()
target_obj.data.shape_keys.use_relative = True
target_obj.data.update_tag()
bpy.context.view_layer.update()
# First smooth boundary areas
smooth_boundary_areas(target_obj, sk_name, mapping, source_data)
# Then smooth penetration areas
smooth_penetration_areas(target_obj, sk_name, mapping, source_data)
def test_shape_key_quality(target_obj, sk_name, mapping, source_data):
"""Test shape key quality with side tracking"""
sk = target_obj.data.shape_keys.key_blocks[sk_name]
print(f" Testing quality of '{sk_name}':")
test_values = [0.0, 0.3, 0.45, 0.5, 0.55, 0.6, 0.65, 0.7, 0.75, 0.77, 0.8, 0.9, 1.0]
prev_positions = None
boundary_vertices = mapping.get('boundary_vertices', set())
for val in test_values:
if val == 0.0:
sk.value = 0.0
else:
target_obj.data.shape_keys.use_relative = False
for i, v in enumerate(sk.data):
if i < len(mapping['target_verts']):
pos = compute_side_preserving_position(i, sk_name, mapping, source_data, val)
v.co = pos
target_obj.data.shape_keys.use_relative = True
target_obj.data.update_tag()
bpy.context.view_layer.update()
if prev_positions is not None:
movements = []
side_changes = 0
boundary_issues = 0
for i, v in enumerate(target_obj.data.vertices):
if i < len(prev_positions) and i < len(mapping['side_flags']):
movement = (v.co - prev_positions[i]).length
if movement > 0.001:
movements.append(movement)
if i < len(mapping['surface_points']) and i < len(mapping['direction_vectors']):
current_to_surface = v.co - mapping['surface_points'][i]
current_side = 1 if current_to_surface.dot(mapping['direction_vectors'][i]) > 0 else -1
if current_side != mapping['side_flags'][i] and mapping['side_flags'][i] != 0:
side_changes += 1
if i in boundary_vertices:
boundary_issues += 1
if movements:
avg_movement = sum(movements) / len(movements)
max_movement = max(movements)
print(f" Value {val:.2f}: avg Δ {avg_movement:.4f}, max Δ {max_movement:.4f}")
if side_changes > 0:
print(f"{side_changes} vertices changed side ({boundary_issues} on boundary)")
prev_positions = [v.co.copy() for v in target_obj.data.vertices]
sk.value = 0.0
target_obj.data.update_tag()
bpy.context.view_layer.update()
def transfer_shape_keys(source_data, target_obj):
"""Transfer shape keys with side preservation"""
print(f" Transferring shape keys with side preservation...")
mapping = compute_robust_surface_mapping(target_obj, source_data)
for sk_name in source_data['names']:
if sk_name == 'Basis':
continue
print(f" Processing: {sk_name}")
set_shape_key_with_side_preservation(target_obj, sk_name, mapping, source_data)
print(f" ✓ Transferred {sk_name}")
return mapping
def verify_transfer(source_names, target_obj):
"""Verify shape keys were transferred correctly"""
target_names = [sk.name for sk in target_obj.data.shape_keys.key_blocks]
print(f" Verification:")
print(f" Source keys: {len(source_names)}")
print(f" Target keys: {len(target_names)}")
if set(source_names) == set(target_names):
print(f" ✓ All shape keys present")
def save_output_file(output_path, temp_target):
"""Save the modified file"""
print(f"\nSaving output file: {output_path}")
output_dir = os.path.dirname(output_path)
if output_dir and not os.path.exists(output_dir):
os.makedirs(output_dir)
bpy.ops.wm.save_as_mainfile(filepath=output_path)
try:
if os.path.exists(temp_target):
os.remove(temp_target)
temp_dir = os.path.dirname(temp_target)
if os.path.exists(temp_dir) and not os.listdir(temp_dir):
os.rmdir(temp_dir)
except:
pass
print(f"File saved successfully")
def main():
print("=" * 60)
print("Blender Shape Key Transfer Script - Boundary Velocity Limiting")
print("=" * 60)
source_file, target_file, output_file = parse_args()
print(f"Source: {source_file}")
print(f"Target: {target_file}")
print(f"Output: {output_file}")
try:
source_data = load_source_data(source_file)
target_objects, temp_target = load_target_file(target_file)
if target_objects:
for idx, target_obj in enumerate(target_objects, 1):
print(f"\n[{idx}/{len(target_objects)}] Processing: {target_obj.name}")
print(f" {'=' * 40}")
ensure_shape_keys_structure(source_data['names'], target_obj)
mapping = transfer_shape_keys(source_data, target_obj)
verify_transfer(source_data['names'], target_obj)
for sk_name in source_data['names']:
if sk_name == 'fat':
test_shape_key_quality(target_obj, sk_name, mapping, source_data)
break
print(f" {'=' * 40}")
print(f" ✓ Completed")
save_output_file(output_file, temp_target)
else:
print("\nNo target objects found with required properties")
print("\n" + "=" * 60)
print("Script completed successfully!")
print("=" * 60)
except Exception as e:
print(f"\nError: {str(e)}")
import traceback
traceback.print_exc()
sys.exit(1)
if __name__ == "__main__":
main()

View File

@@ -201,16 +201,15 @@ BoatModule::BoatModule(flecs::world &ecs)
->_getDerivedOrientation();
if (ev.e2.has<
CharacterBase>()) {
ev.e2.get_mut<
CharacterBase>()
.mBodyNode
->_setDerivedPosition(
position);
ev.e2.get_mut<
CharacterBase>()
.mBodyNode
->_setDerivedOrientation(
orientation);
Ogre::SceneNode *n =
ECS::get<
CharacterModule>()
.characterNodes
.at(ev.e2);
n->_setDerivedPosition(
position);
n->_setDerivedOrientation(
orientation);
}
}
e.set<BoatCurrentActuator>(
@@ -282,14 +281,14 @@ BoatModule::BoatModule(flecs::world &ecs)
captainSeat
->_getDerivedOrientation();
if (ev.e2.has<CharacterBase>()) {
ev.e2.get_mut<CharacterBase>()
.mBodyNode
->_setDerivedPosition(
position);
ev.e2.get_mut<CharacterBase>()
.mBodyNode
->_setDerivedOrientation(
orientation);
Ogre::SceneNode *n =
ECS::get<CharacterModule>()
.characterNodes
.at(ev.e2);
n->_setDerivedPosition(
position);
n->_setDerivedOrientation(
orientation);
}
} else if (ev.event == "boat_control_exit") {
} else if (ev.event == "actuator_exit") {

View File

@@ -188,17 +188,16 @@ public:
int update(float delta) override
{
if (npc.e.is_valid()) {
Ogre::SceneNode *n =
ECS::get<CharacterModule>()
.characterNodes.at(npc.e);
Ogre::Vector3 position =
npc.e.get<CharacterBase>()
.mBodyNode
->_getDerivedPosition();
n->_getDerivedPosition();
if (position.squaredDistance(targetPosition) >=
radius * radius) {
if (npc.e.is_valid())
npc.e.get<CharacterBase>()
.mBodyNode
->_setDerivedPosition(
targetPosition);
n->_setDerivedPosition(
targetPosition);
npc.position = targetPosition;
return BUSY;
}
@@ -588,11 +587,13 @@ CharacterAIModule::CharacterAIModule(flecs::world &ecs)
it++) {
auto &npc = npcs.npcs.at(it->first);
if (npc.e.is_valid() &&
npc.e.has<CharacterBase>())
npc.position =
npc.e.get<CharacterBase>()
.mBodyNode
->_getDerivedPosition();
npc.e.has<CharacterBase>()) {
Ogre::SceneNode *n =
ECS::get<CharacterModule>()
.characterNodes.at(
npc.e);
npc.position = n->_getDerivedPosition();
}
}
});
ecs.system<ActionNodeList, TownAI, TownNPCs>("UpdateBlackboards")
@@ -1214,11 +1215,12 @@ void Blackboard::query_ai()
const float distance = 10000.0f;
Ogre::Vector3 position(0, 0, 0);
if (npcs.npcs.at(index).e.is_valid() &&
npcs.npcs.at(index).e.has<CharacterBase>())
position = npcs.npcs.at(index)
.e.get<CharacterBase>()
.mBodyNode->_getDerivedPosition();
else
npcs.npcs.at(index).e.has<CharacterBase>()) {
Ogre::SceneNode *n =
ECS::get<CharacterModule>().characterNodes.at(
npcs.npcs.at(index).e);
position = n->_getDerivedPosition();
} else
from_json(npcs.npcs.at(index).props["position"], position);
this->position = position;
ActionNodeList &alist = ECS::get_mut<ActionNodeList>();

View File

@@ -29,8 +29,11 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs)
if (!anim.configured) {
int i, j;
e.set<EventData>({});
Ogre::SceneNode *n =
ECS::get<CharacterModule>()
.characterNodes.at(e);
Ogre::Entity *ent = static_cast<Ogre::Entity *>(
ch.mBodyNode->getAttachedObject(0));
n->getAttachedObject(0));
ent->getSkeleton()->setBlendMode(
Ogre::ANIMBLEND_CUMULATIVE);
Ogre::AnimationStateSet *animStateSet =
@@ -197,10 +200,15 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs)
ZoneScopedN("HandleRootMotionVelocity");
if (eng.delta < 0.0000001f)
return;
if (!ch.mBodyNode)
if (ECS::get<CharacterModule>().characterNodes.find(
e) ==
ECS::get<CharacterModule>().characterNodes.end())
return;
Ogre::Quaternion rot = ch.mBodyNode->getOrientation();
Ogre::Vector3 pos = ch.mBodyNode->getPosition();
const Ogre::SceneNode *n =
ECS::get<CharacterModule>().characterNodes.at(
e);
Ogre::Quaternion rot = n->getOrientation();
Ogre::Vector3 pos = n->getPosition();
Ogre::Vector3 boneMotion = ch.mBoneMotion;
v.velocity = Ogre::Vector3::ZERO;
if (eng.delta <= 0.005)
@@ -235,7 +243,7 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs)
v.velocity.y = 0.0f;
#endif
OgreAssert(v.velocity.squaredLength() < 1000.0f,
"Bad velocity setting");
"Bad velocity setting " + Ogre::StringConverter::toString(safeDelta) + " " + Ogre::StringConverter::toString(boneMotion));
});
ecs.system<const EngineData, CharacterBase, AnimationControl,
CharacterVelocity>("HandleRootMotion")
@@ -244,8 +252,10 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs)
CharacterBase &ch, AnimationControl &anim,
CharacterVelocity &v) {
ZoneScopedN("HandleRootMotion");
#if 0
if (!ch.mBodyNode)
return;
#endif
if (eng.delta < 0.0000001f)
return;
OgreAssert(eng.delta > 0.0f, "Zero delta");

View File

@@ -21,8 +21,9 @@ void createNPCActionNodes(flecs::entity town, int index)
flecs::entity e = npc.e;
nlohmann::json npcprops = npc.props;
const CharacterBase &ch = e.get<CharacterBase>();
Ogre::Vector3 characterPos = ch.mBodyNode->_getDerivedPosition();
Ogre::Quaternion characterRot = ch.mBodyNode->_getDerivedOrientation();
Ogre::SceneNode *n = ECS::get<CharacterModule>().characterNodes.at(e);
Ogre::Vector3 characterPos = n->_getDerivedPosition();
Ogre::Quaternion characterRot = n->_getDerivedOrientation();
if (npc.actionNodes.size() > 0) {
int i;
for (i = 0; i < npc.actionNodes.size(); i++) {
@@ -117,8 +118,8 @@ CharacterManagerModule::CharacterManagerModule(flecs::world &ecs)
if (!player.has<CharacterBase>())
return;
Ogre::Vector3 cameraPos =
player.get<CharacterBase>()
.mBodyNode->_getDerivedPosition();
ECS::get<Camera>()
.mCameraNode->_getDerivedPosition();
for (auto &npc : npcs.npcs) {
int index = npc.first;
TownNPCs::NPCData &data = npc.second;
@@ -140,6 +141,7 @@ CharacterManagerModule::CharacterManagerModule(flecs::world &ecs)
break;
}
}
#if 0
if (cameraPos.squaredDistance(npcPosition) >
22500.0f) {
if (data.e.is_valid()) {
@@ -148,6 +150,7 @@ CharacterManagerModule::CharacterManagerModule(flecs::world &ecs)
break;
}
}
#endif
}
for (auto &npc : npcs.npcs) {
int index = npc.first;

View File

@@ -162,9 +162,9 @@ CharacterModule::CharacterModule(flecs::world &ecs)
OgreAssert(characterOrientations.find(e) !=
characterOrientations.end(),
"Bad orientation/position");
ch.mBodyNode = characterNodes[e];
ch.mBodyNode->setOrientation(characterOrientations[e]);
ch.mBodyNode->setPosition(characterPositions[e]);
Ogre::SceneNode *bodyNode = characterNodes[e];
bodyNode->setOrientation(characterOrientations[e]);
bodyNode->setPosition(characterPositions[e]);
OgreAssert(
characterEntitiesFace[e]->getSkeleton()->hasBone(
"Root"),
@@ -304,7 +304,9 @@ CharacterModule::CharacterModule(flecs::world &ecs)
ch.mGoalDirection.normalise();
Ogre::Quaternion toGoal =
ch.mBodyNode->getOrientation()
ECS::get<CharacterModule>()
.characterNodes.at(e)
->getOrientation()
.zAxis()
.getRotationTo(
ch.mGoalDirection);
@@ -329,7 +331,9 @@ CharacterModule::CharacterModule(flecs::world &ecs)
std::min<Ogre::Real>(
yawToGoal,
yawAtSpeed)); //yawToGoal = Math::Clamp<Real>(yawToGoal, 0, yawAtSpeed);
ch.mBodyNode->yaw(Ogre::Degree(yawToGoal));
ECS::get<CharacterModule>()
.characterNodes.at(e)
->yaw(Ogre::Degree(yawToGoal));
}
});
#if 0
@@ -363,7 +367,7 @@ CharacterModule::CharacterModule(flecs::world &ecs)
.kind(flecs::OnUpdate)
.with<Player>()
.with<GroundCheckReady>()
.each([](const EngineData &eng, Camera &camera,
.each([](flecs::entity e, const EngineData &eng, Camera &camera,
const CharacterBase &ch) {
ZoneScopedN("UpdateCamera");
float delta = eng.delta;
@@ -391,7 +395,9 @@ CharacterModule::CharacterModule(flecs::world &ecs)
} else {
// place the camera pivot roughly at the character's shoulder
camera.mCameraPivot->setPosition(
ch.mBodyNode->getPosition() +
ECS::get<CharacterModule>()
.characterNodes.at(e)
->getPosition() +
Ogre::Vector3::UNIT_Y * CAM_HEIGHT);
// move the camera smoothly to the goal
Ogre::Vector3 goalOffset =

View File

@@ -23,7 +23,6 @@ struct CharacterBase {
Ogre::Vector3 mBoneMotion;
Ogre::Vector3 mBonePrevMotion;
Ogre::Vector3 mGoalDirection; // actual intended direction in world-space
Ogre::SceneNode *mBodyNode;
bool is_submerged;
};
struct CharacterLocation {

View File

@@ -457,34 +457,35 @@ LuaData::LuaData()
Ogre::Vector3 position = target_node->_getDerivedPosition();
Ogre::Quaternion orientation =
target_node->_getDerivedOrientation();
if (object_e.has<CharacterBase>()) {
object_e.get_mut<CharacterBase>()
.mBodyNode->_setDerivedPosition(position);
object_e.get_mut<CharacterBase>()
.mBodyNode->_setDerivedOrientation(orientation);
}
return 0;
});
lua_setglobal(L, "ecs_trigger_set_position");
lua_pushcfunction(L, [](lua_State *L) -> int {
luaL_checktype(L, 1, LUA_TNUMBER); // trigger
int trigger = lua_tointeger(L, 1);
flecs::entity trigger_e = idmap.get_entity(trigger);
Ogre::SceneNode *node = trigger_e.get<EventTrigger>().node;
Ogre::Any animationAny =
node->getUserObjectBindings().getUserAny(
"trigger_animation");
if (animationAny.has_value()) {
Ogre::String animation =
Ogre::any_cast<Ogre::String>(animationAny);
lua_pushstring(L, animation.c_str());
if (object_e.has<CharacterBase>()) {
Ogre::SceneNode *bodyNode =
ECS::get<CharacterModule>().characterNodes.at(
object_e);
bodyNode->_setDerivedPosition(position);
bodyNode->_setDerivedOrientation(orientation);
}
return 0;
});
lua_setglobal(L, "ecs_trigger_set_position");
lua_pushcfunction(L, [](lua_State *L) -> int {
luaL_checktype(L, 1, LUA_TNUMBER); // trigger
int trigger = lua_tointeger(L, 1);
flecs::entity trigger_e = idmap.get_entity(trigger);
Ogre::SceneNode *node = trigger_e.get<EventTrigger>().node;
Ogre::Any animationAny =
node->getUserObjectBindings().getUserAny(
"trigger_animation");
if (animationAny.has_value()) {
Ogre::String animation =
Ogre::any_cast<Ogre::String>(animationAny);
lua_pushstring(L, animation.c_str());
return 1;
}
lua_pushnil(L);
return 1;
});
lua_setglobal(L, "ecs_trigger_get_animation");
lua_pushcfunction(L, [](lua_State *L) -> int {
}
lua_pushnil(L);
return 1;
});
lua_setglobal(L, "ecs_trigger_get_animation");
lua_pushcfunction(L, [](lua_State *L) -> int {
OgreAssert(lua_gettop(L) == 8, "Invalid parameters");
luaL_checktype(L, 1, LUA_TSTRING); // face
luaL_checktype(L, 2, LUA_TSTRING); // hair
@@ -504,101 +505,103 @@ LuaData::LuaData()
float x = lua_tonumber(L, 5);
float y = lua_tonumber(L, 6);
float z = lua_tonumber(L, 7);
Ogre::Quaternion orientation(Ogre::Radian(yaw),
Ogre::Vector3::UNIT_Y);
Ogre::Vector3 npcPos(x, y, z);
flecs::entity e =
ECS::get_mut<CharacterManagerModule>()
Ogre::Quaternion orientation(Ogre::Radian(yaw),
Ogre::Vector3::UNIT_Y);
Ogre::Vector3 npcPos(x, y, z);
flecs::entity e =
ECS::get_mut<CharacterManagerModule>()
.createCharacterData(face, hair, top, bottom,
feet, npcPos, orientation);
ECS::modified<CharacterManagerModule>();
lua_pushinteger(L, idmap.add_entity(e));
return 1;
});
lua_setglobal(L, "ecs_npc_set");
lua_pushcfunction(L, [](lua_State *L) -> int {
OgreAssert(lua_gettop(L) == 1, "Bad parameters");
luaL_checktype(L, 1, LUA_TSTRING); // type
const char *fileName = lua_tostring(L, 1);
ECS::get<EngineData>().mScnMgr->getRootSceneNode()->saveChildren(
fileName);
return 0;
});
lua_setglobal(L, "ecs_save_scene_debug");
lua_pushcfunction(L, [](lua_State *L) -> int {
OgreAssert(lua_gettop(L) == 2, "Bad parameters");
luaL_checktype(L, 1, LUA_TNUMBER); // object
luaL_checktype(L, 2, LUA_TSTRING); // name
int object = lua_tointeger(L, 1);
flecs::entity object_e = idmap.get_entity(object);
const char *fileName = lua_tostring(L, 2);
Ogre::SceneNode *node = nullptr;
if (object_e.has<CharacterBase>())
node = object_e.get<CharacterBase>().mBodyNode;
else if (object_e.has<BoatBase>())
node = object_e.get<BoatBase>().mNode;
if (node)
node->saveChildren(fileName);
return 0;
});
lua_setglobal(L, "ecs_save_object_debug");
lua_pushcfunction(L, [](lua_State *L) -> int {
luaL_checktype(L, 1, LUA_TBOOLEAN); // object
ECS::get_mut<EngineData>().enableDbgDraw = lua_toboolean(L, 1);
ECS::modified<EngineData>();
return 0;
});
lua_setglobal(L, "ecs_set_debug_drawing");
lua_pushcfunction(L, [](lua_State *L) -> int {
OgreAssert(lua_gettop(L) >= 1, "Bad parameters");
luaL_checktype(L, 1, LUA_TSTRING);
Ogre::String command = lua_tostring(L, 1);
if (command == "physics-control") {
OgreAssert(lua_gettop(L) == 3, "Bad parameters");
luaL_checktype(L, 2, LUA_TNUMBER); // object
luaL_checktype(L, 3, LUA_TBOOLEAN);
int object = lua_tointeger(L, 2);
flecs::entity object_e = idmap.get_entity(object);
bool enable = lua_toboolean(L, 3);
OgreAssert(object_e.has<CharacterBase>(),
"Not a character");
PhysicsModule::controlPhysics(object_e, enable);
object_e.add<CharacterUpdatePhysicsState>();
ECS::modified<CharacterManagerModule>();
lua_pushinteger(L, idmap.add_entity(e));
return 1;
});
lua_setglobal(L, "ecs_npc_set");
lua_pushcfunction(L, [](lua_State *L) -> int {
OgreAssert(lua_gettop(L) == 1, "Bad parameters");
luaL_checktype(L, 1, LUA_TSTRING); // type
const char *fileName = lua_tostring(L, 1);
ECS::get<EngineData>().mScnMgr->getRootSceneNode()->saveChildren(
fileName);
return 0;
});
lua_setglobal(L, "ecs_save_scene_debug");
lua_pushcfunction(L, [](lua_State *L) -> int {
OgreAssert(lua_gettop(L) == 2, "Bad parameters");
luaL_checktype(L, 1, LUA_TNUMBER); // object
luaL_checktype(L, 2, LUA_TSTRING); // name
int object = lua_tointeger(L, 1);
flecs::entity object_e = idmap.get_entity(object);
const char *fileName = lua_tostring(L, 2);
Ogre::SceneNode *node = nullptr;
if (object_e.has<CharacterBase>()) {
node = ECS::get<CharacterModule>().characterNodes.at(
object_e);
} else if (object_e.has<BoatBase>())
node = object_e.get<BoatBase>().mNode;
if (node)
node->saveChildren(fileName);
return 0;
});
lua_setglobal(L, "ecs_save_object_debug");
lua_pushcfunction(L, [](lua_State *L) -> int {
luaL_checktype(L, 1, LUA_TBOOLEAN); // object
ECS::get_mut<EngineData>().enableDbgDraw = lua_toboolean(L, 1);
ECS::modified<EngineData>();
return 0;
});
lua_setglobal(L, "ecs_set_debug_drawing");
lua_pushcfunction(L, [](lua_State *L) -> int {
OgreAssert(lua_gettop(L) >= 1, "Bad parameters");
luaL_checktype(L, 1, LUA_TSTRING);
Ogre::String command = lua_tostring(L, 1);
if (command == "physics-control") {
OgreAssert(lua_gettop(L) == 3, "Bad parameters");
luaL_checktype(L, 2, LUA_TNUMBER); // object
luaL_checktype(L, 3, LUA_TBOOLEAN);
int object = lua_tointeger(L, 2);
flecs::entity object_e = idmap.get_entity(object);
bool enable = lua_toboolean(L, 3);
OgreAssert(object_e.has<CharacterBase>(),
"Not a character");
PhysicsModule::controlPhysics(object_e, enable);
object_e.add<CharacterUpdatePhysicsState>();
return 0;
} else if (command == "is-player") {
} else if (command == "is-player") {
OgreAssert(lua_gettop(L) == 2, "Bad parameters");
luaL_checktype(L, 2, LUA_TNUMBER); // object
int object = lua_tointeger(L, 2);
luaL_checktype(L, 2, LUA_TNUMBER); // object
int object = lua_tointeger(L, 2);
flecs::entity object_e = idmap.get_entity(object);
lua_pushboolean(L, object_e.has<Player>());
return 1;
} else if (command == "set-actuator") {
OgreAssert(lua_gettop(L) == 3, "Bad parameters");
luaL_checktype(L, 2, LUA_TNUMBER); // object
luaL_checktype(L, 3, LUA_TSTRING); // animation
Ogre::String animation = lua_tostring(L, 3);
lua_pushboolean(L, object_e.has<Player>());
return 1;
} else if (command == "set-actuator") {
OgreAssert(lua_gettop(L) == 3, "Bad parameters");
luaL_checktype(L, 2, LUA_TNUMBER); // object
luaL_checktype(L, 3, LUA_TSTRING); // animation
Ogre::String animation = lua_tostring(L, 3);
int object = lua_tointeger(L, 2);
flecs::entity object_e = idmap.get_entity(object);
object_e.set<CharacterVelocity>(
{ { 0, 0, 0 }, { 0, 0, 0 } });
if (animation.length() > 0)
object_e.set<CharacterInActuator>(
{ animation, { 0, 0, 0 } });
else
object_e.remove<CharacterInActuator>();
int object = lua_tointeger(L, 2);
flecs::entity object_e = idmap.get_entity(object);
object_e.set<CharacterVelocity>(
{ { 0, 0, 0 }, { 0, 0, 0 } });
if (animation.length() > 0)
object_e.set<CharacterInActuator>(
{ animation, { 0, 0, 0 } });
else
object_e.remove<CharacterInActuator>();
return 0;
} else if (command == "animation-state") {
OgreAssert(lua_gettop(L) >= 4, "Bad parameters");
luaL_checktype(L, 2, LUA_TNUMBER); // object
luaL_checktype(L, 3, LUA_TSTRING); // node
luaL_checktype(L, 4, LUA_TSTRING); // state
if (lua_gettop(L) == 5)
luaL_checktype(L, 5, LUA_TBOOLEAN); // reset
int object = lua_tointeger(L, 2);
flecs::entity object_e = idmap.get_entity(object);
Ogre::String nodeName = lua_tostring(L, 3);
Ogre::String stateName = lua_tostring(L, 4);
} else if (command == "animation-state") {
OgreAssert(lua_gettop(L) >= 4, "Bad parameters");
luaL_checktype(L, 2, LUA_TNUMBER); // object
luaL_checktype(L, 3, LUA_TSTRING); // node
luaL_checktype(L, 4, LUA_TSTRING); // state
if (lua_gettop(L) == 5)
luaL_checktype(L, 5,
LUA_TBOOLEAN); // reset
int object = lua_tointeger(L, 2);
flecs::entity object_e = idmap.get_entity(object);
Ogre::String nodeName = lua_tostring(L, 3);
Ogre::String stateName = lua_tostring(L, 4);
#if 0
bool reset = false;
if (lua_gettop(L) == 5)
@@ -622,183 +625,183 @@ LuaData::LuaData()
#endif
OgreAssert(false, "Not implemented");
return 0;
} else if (command == "params-set") {
OgreAssert(lua_gettop(L) == 4, "Invalid parameters");
luaL_checktype(L, 2, LUA_TNUMBER);
luaL_checktype(L, 3, LUA_TSTRING);
luaL_checktype(L, 4, LUA_TBOOLEAN);
bool enable = lua_toboolean(L, 4);
flecs::entity e = idmap.get_entity(lua_tointeger(L, 2));
Ogre::String what = lua_tostring(L, 3);
OgreAssert(e.is_valid(), "Invalid character");
OgreAssert(e.has<Character>(), "Not a character");
if (what == "gravity") {
/* clear momentum */
if (e.has<CharacterVelocity>()) {
e.get_mut<CharacterVelocity>()
.gvelocity.y = 0.0f;
e.get_mut<CharacterVelocity>()
.velocity.y = 0.0f;
e.modified<CharacterVelocity>();
}
if (enable)
e.add<CharacterGravity>();
else
e.remove<CharacterGravity>();
} else if (what == "buoyancy") {
if (enable)
e.add<CharacterBuoyancy>();
else
e.remove<CharacterBuoyancy>();
} else
OgreAssert(false, "Bad parameter " + what);
return 0;
} else {
OgreAssert(false, "bad argument " + command);
return 0;
}
});
lua_setglobal(L, "ecs_character");
lua_pushcfunction(L, [](lua_State *L) -> int {
OgreAssert(lua_gettop(L) == 3, "Bad parameters");
luaL_checktype(L, 1, LUA_TNUMBER); // parent
luaL_checktype(L, 2, LUA_TNUMBER); // object
luaL_checktype(L, 3, LUA_TSTRING); // slot
int parent = lua_tointeger(L, 1);
int object = lua_tointeger(L, 2);
Ogre::String slot = lua_tostring(L, 3);
flecs::entity parent_e = idmap.get_entity(parent);
flecs::entity object_e = idmap.get_entity(object);
PhysicsModule::controlPhysics(object_e, false);
object_e.set<ParentSlot>({ parent_e, slot });
return 0;
});
lua_setglobal(L, "ecs_set_slot");
lua_pushcfunction(L, [](lua_State *L) -> int {
flecs::entity e = ECS::get().lookup("player");
int result = idmap.add_entity(e);
lua_pushinteger(L, result);
return result;
});
lua_setglobal(L, "ecs_get_player_entity");
lua_pushcfunction(L, [](lua_State *L) -> int {
luaL_checktype(L, 1, LUA_TNUMBER); // entity id
int id = lua_tointeger(L, 1);
flecs::entity e = idmap.get_entity(id);
LuaEcsEntity *edata = static_cast<LuaEcsEntity *>(
lua_newuserdata(L, sizeof(LuaEcsEntity)));
new (edata) LuaEcsEntity();
edata->e = e;
edata->id = e;
luaL_getmetatable(L, "FlecsEntityMetaTable");
lua_setmetatable(L, -2);
return 1;
});
lua_setglobal(L, "ecs_get_entity");
luaL_newmetatable(L, "FlecsEntityMetaTable");
lua_pushstring(L, "__index");
lua_pushcfunction(L, [](lua_State *L) -> int {
luaL_checktype(L, 1, LUA_TUSERDATA); //metatable
luaL_checktype(L, 2, LUA_TSTRING); //function
Ogre::String component = lua_tostring(L, 2);
if (component == "components") {
lua_pushvalue(L, 1);
lua_pushcclosure(
L,
[](lua_State *L) -> int {
luaL_checktype(L, lua_upvalueindex(1),
LUA_TUSERDATA);
LuaEcsEntity *ent = static_cast<
LuaEcsEntity *>(lua_touserdata(
L, lua_upvalueindex(1)));
std::cout << ent->id << " called!!!\n";
ent->e.each([&](flecs::id id) {
flecs::entity cmp =
ECS::get().entity(id);
if (cmp.is_alive()) {
const char *name =
cmp.name();
if (name)
std::cout
<< "component: "
<< name
<< std::endl;
}
});
return 0;
},
1);
} else if (component == "is_trigger") {
lua_pushvalue(L, 1);
lua_pushcclosure(
L,
[](lua_State *L) -> int {
luaL_checktype(L, lua_upvalueindex(1),
LUA_TUSERDATA);
LuaEcsEntity *ent = static_cast<
LuaEcsEntity *>(lua_touserdata(
L, lua_upvalueindex(1)));
lua_pushboolean(
L, ent->e.has<EventTrigger>());
return 1;
},
1);
} else if (component == "is_character") {
lua_pushvalue(L, 1);
lua_pushcclosure(
L,
[](lua_State *L) -> int {
luaL_checktype(L, lua_upvalueindex(1),
LUA_TUSERDATA);
LuaEcsEntity *ent = static_cast<
LuaEcsEntity *>(lua_touserdata(
L, lua_upvalueindex(1)));
lua_pushboolean(
L, ent->e.has<Character>());
return 1;
},
1);
} else if (component == "is_boat") {
lua_pushvalue(L, 1);
lua_pushcclosure(
L,
[](lua_State *L) -> int {
luaL_checktype(L, lua_upvalueindex(1),
LUA_TUSERDATA);
LuaEcsEntity *ent = static_cast<
LuaEcsEntity *>(lua_touserdata(
L, lua_upvalueindex(1)));
lua_pushboolean(L,
return 0;
} else if (command == "params-set") {
OgreAssert(lua_gettop(L) == 4, "Invalid parameters");
luaL_checktype(L, 2, LUA_TNUMBER);
luaL_checktype(L, 3, LUA_TSTRING);
luaL_checktype(L, 4, LUA_TBOOLEAN);
bool enable = lua_toboolean(L, 4);
flecs::entity e = idmap.get_entity(lua_tointeger(L, 2));
Ogre::String what = lua_tostring(L, 3);
OgreAssert(e.is_valid(), "Invalid character");
OgreAssert(e.has<Character>(), "Not a character");
if (what == "gravity") {
/* clear momentum */
if (e.has<CharacterVelocity>()) {
e.get_mut<CharacterVelocity>()
.gvelocity.y = 0.0f;
e.get_mut<CharacterVelocity>()
.velocity.y = 0.0f;
e.modified<CharacterVelocity>();
}
if (enable)
e.add<CharacterGravity>();
else
e.remove<CharacterGravity>();
} else if (what == "buoyancy") {
if (enable)
e.add<CharacterBuoyancy>();
else
e.remove<CharacterBuoyancy>();
} else
OgreAssert(false, "Bad parameter " + what);
return 0;
} else {
OgreAssert(false, "bad argument " + command);
return 0;
}
});
lua_setglobal(L, "ecs_character");
lua_pushcfunction(L, [](lua_State *L) -> int {
OgreAssert(lua_gettop(L) == 3, "Bad parameters");
luaL_checktype(L, 1, LUA_TNUMBER); // parent
luaL_checktype(L, 2, LUA_TNUMBER); // object
luaL_checktype(L, 3, LUA_TSTRING); // slot
int parent = lua_tointeger(L, 1);
int object = lua_tointeger(L, 2);
Ogre::String slot = lua_tostring(L, 3);
flecs::entity parent_e = idmap.get_entity(parent);
flecs::entity object_e = idmap.get_entity(object);
PhysicsModule::controlPhysics(object_e, false);
object_e.set<ParentSlot>({ parent_e, slot });
return 0;
});
lua_setglobal(L, "ecs_set_slot");
lua_pushcfunction(L, [](lua_State *L) -> int {
flecs::entity e = ECS::get().lookup("player");
int result = idmap.add_entity(e);
lua_pushinteger(L, result);
return result;
});
lua_setglobal(L, "ecs_get_player_entity");
lua_pushcfunction(L, [](lua_State *L) -> int {
luaL_checktype(L, 1, LUA_TNUMBER); // entity id
int id = lua_tointeger(L, 1);
flecs::entity e = idmap.get_entity(id);
LuaEcsEntity *edata = static_cast<LuaEcsEntity *>(
lua_newuserdata(L, sizeof(LuaEcsEntity)));
new (edata) LuaEcsEntity();
edata->e = e;
edata->id = e;
luaL_getmetatable(L, "FlecsEntityMetaTable");
lua_setmetatable(L, -2);
return 1;
});
lua_setglobal(L, "ecs_get_entity");
luaL_newmetatable(L, "FlecsEntityMetaTable");
lua_pushstring(L, "__index");
lua_pushcfunction(L, [](lua_State *L) -> int {
luaL_checktype(L, 1, LUA_TUSERDATA); //metatable
luaL_checktype(L, 2, LUA_TSTRING); //function
Ogre::String component = lua_tostring(L, 2);
if (component == "components") {
lua_pushvalue(L, 1);
lua_pushcclosure(
L,
[](lua_State *L) -> int {
luaL_checktype(L, lua_upvalueindex(1),
LUA_TUSERDATA);
LuaEcsEntity *ent = static_cast<
LuaEcsEntity *>(lua_touserdata(
L, lua_upvalueindex(1)));
std::cout << ent->id << " called!!!\n";
ent->e.each([&](flecs::id id) {
flecs::entity cmp =
ECS::get().entity(id);
if (cmp.is_alive()) {
const char *name =
cmp.name();
if (name)
std::cout
<< "component: "
<< name
<< std::endl;
}
});
return 0;
},
1);
} else if (component == "is_trigger") {
lua_pushvalue(L, 1);
lua_pushcclosure(
L,
[](lua_State *L) -> int {
luaL_checktype(L, lua_upvalueindex(1),
LUA_TUSERDATA);
LuaEcsEntity *ent = static_cast<
LuaEcsEntity *>(lua_touserdata(
L, lua_upvalueindex(1)));
lua_pushboolean(
L, ent->e.has<EventTrigger>());
return 1;
},
1);
} else if (component == "is_character") {
lua_pushvalue(L, 1);
lua_pushcclosure(
L,
[](lua_State *L) -> int {
luaL_checktype(L, lua_upvalueindex(1),
LUA_TUSERDATA);
LuaEcsEntity *ent = static_cast<
LuaEcsEntity *>(lua_touserdata(
L, lua_upvalueindex(1)));
lua_pushboolean(
L, ent->e.has<Character>());
return 1;
},
1);
} else if (component == "is_boat") {
lua_pushvalue(L, 1);
lua_pushcclosure(
L,
[](lua_State *L) -> int {
luaL_checktype(L, lua_upvalueindex(1),
LUA_TUSERDATA);
LuaEcsEntity *ent = static_cast<
LuaEcsEntity *>(lua_touserdata(
L, lua_upvalueindex(1)));
lua_pushboolean(L,
ent->e.has<BoatBase>());
return 1;
},
1);
} else if (component == "is_player") {
lua_pushvalue(L, 1);
lua_pushcclosure(
L,
[](lua_State *L) -> int {
luaL_checktype(L, lua_upvalueindex(1),
LUA_TUSERDATA);
LuaEcsEntity *ent = static_cast<
LuaEcsEntity *>(lua_touserdata(
L, lua_upvalueindex(1)));
lua_pushboolean(L,
return 1;
},
1);
} else if (component == "is_player") {
lua_pushvalue(L, 1);
lua_pushcclosure(
L,
[](lua_State *L) -> int {
luaL_checktype(L, lua_upvalueindex(1),
LUA_TUSERDATA);
LuaEcsEntity *ent = static_cast<
LuaEcsEntity *>(lua_touserdata(
L, lua_upvalueindex(1)));
lua_pushboolean(L,
ent->e.has<Player>());
return 1;
},
1);
} else
lua_pushnil(L);
return 1;
});
lua_settable(L, -3);
return 1;
},
1);
} else
lua_pushnil(L);
return 1;
});
lua_settable(L, -3);
}
LuaData::~LuaData()
{
lua_close(L);
lua_close(L);
}
void LuaData::lateSetup()
@@ -817,96 +820,95 @@ void LuaData::lateSetup()
}
}
#endif
Ogre::DataStreamPtr stream =
Ogre::ResourceGroupManager::getSingleton().openResource(
"data.lua", "LuaScripts");
std::cout << "stream: " << stream->getAsString() << "\n";
if (luaL_dostring(L, stream->getAsString().c_str()) != LUA_OK) {
std::cout << "error: " << lua_tostring(L, -1) << "\n";
OgreAssert(false, "Script failure");
}
Ogre::DataStreamPtr stream =
Ogre::ResourceGroupManager::getSingleton().openResource(
"data.lua", "LuaScripts");
std::cout << "stream: " << stream->getAsString() << "\n";
if (luaL_dostring(L, stream->getAsString().c_str()) != LUA_OK) {
std::cout << "error: " << lua_tostring(L, -1) << "\n";
OgreAssert(false, "Script failure");
}
const char *lua_code = "\n\
const char *lua_code = "\n\
function stuff()\n\
return 4\n\
end\n\
x = stuff()\n\
";
luaL_dostring(L, lua_code);
lua_getglobal(L, "x");
int x = lua_tonumber(L, 1);
std::cout << "lua: " << x << "\n";
luaL_dostring(L, lua_code);
lua_getglobal(L, "x");
int x = lua_tonumber(L, 1);
std::cout << "lua: " << x << "\n";
}
LuaModule::LuaModule(flecs::world &ecs)
{
ecs.module<LuaModule>();
ecs.import <SlotsModule>();
ecs.import <VehicleManagerModule>();
ecs.module<LuaModule>();
ecs.import <SlotsModule>();
ecs.import <VehicleManagerModule>();
ecs.import <PlayerActionModule>();
ecs.component<LuaChildEventTrigger>();
ecs.component<LuaBase>()
.on_add([](LuaBase &lua) {
lua.mLua = new LuaData;
lua.setup_called = false;
lua.startup_called = false;
})
.add(flecs::Singleton);
ecs.component<LuaEvent>().add(flecs::Singleton);
ecs.system<const EngineData, LuaBase>("LuaUpdate")
.kind(flecs::OnUpdate)
.each([](const EngineData &eng, LuaBase &lua) {
if (!lua.setup_called) {
lua.mLua->lateSetup();
lua.mLua->call_handler("setup");
lua.setup_called = true;
}
if (!lua.startup_called) {
if (eng.startupDelay <= 0.0f) {
lua.mLua->call_handler("startup");
lua.startup_called = true;
ecs.component<LuaChildEventTrigger>();
ecs.component<LuaBase>()
.on_add([](LuaBase &lua) {
lua.mLua = new LuaData;
lua.setup_called = false;
lua.startup_called = false;
})
.add(flecs::Singleton);
ecs.component<LuaEvent>().add(flecs::Singleton);
ecs.system<const EngineData, LuaBase>("LuaUpdate")
.kind(flecs::OnUpdate)
.each([](const EngineData &eng, LuaBase &lua) {
if (!lua.setup_called) {
lua.mLua->lateSetup();
lua.mLua->call_handler("setup");
lua.setup_called = true;
}
if (!lua.startup_called) {
if (eng.startupDelay <= 0.0f) {
lua.mLua->call_handler("startup");
lua.startup_called = true;
}
}
});
ecs.system<const EngineData, const LuaChildEventTrigger>(
"CreateChildTrigger")
.kind(flecs::OnUpdate)
.without<EventTrigger>()
.write<EventTrigger>()
.each([](flecs::entity e, const EngineData &env,
const LuaChildEventTrigger &lct) {
Ogre::SceneNode *parentNode = nullptr;
flecs::entity parent_e = lct.parent_e;
if (parent_e.has<CharacterBase>()) {
parentNode =
parent_e.get<CharacterBase>().mBodyNode;
OgreAssert(
parent_e.get<CharacterBase>().mBodyNode,
"bad node");
}
});
ecs.system<const EngineData, const LuaChildEventTrigger>(
"CreateChildTrigger")
.kind(flecs::OnUpdate)
.without<EventTrigger>()
.write<EventTrigger>()
.each([](flecs::entity e, const EngineData &env,
const LuaChildEventTrigger &lct) {
Ogre::SceneNode *parentNode = nullptr;
flecs::entity parent_e = lct.parent_e;
if (parent_e.has<CharacterBase>()) {
parentNode =
ECS::get<CharacterModule>()
.characterNodes.at(parent_e);
OgreAssert(parentNode, "bad node");
} else if (parent_e.has<BoatBase>()) {
parentNode = parent_e.get<BoatBase>().mNode;
OgreAssert(parent_e.get<BoatBase>().mNode,
"bad node");
} else
return;
EventTrigger &trigger = e.ensure<EventTrigger>();
OgreAssert(parentNode, "bad parent");
trigger.position = lct.position;
trigger.halfheight = lct.halfheight;
trigger.radius = lct.radius;
trigger.event = lct.event;
trigger.parent = parentNode;
e.modified<EventTrigger>();
});
ecs.system<LuaBase, LuaEvent>("HandleLuaEvents")
.kind(flecs::OnUpdate)
.each([](LuaBase &base, LuaEvent &evt) {
while (!evt.events.empty()) {
LuaEvent::Event &ev = evt.events.front();
base.mLua->call_handler(ev.event, ev.e1, ev.e2);
evt.events.pop_front();
}
});
} else if (parent_e.has<BoatBase>()) {
parentNode = parent_e.get<BoatBase>().mNode;
OgreAssert(parent_e.get<BoatBase>().mNode,
"bad node");
} else
return;
EventTrigger &trigger = e.ensure<EventTrigger>();
OgreAssert(parentNode, "bad parent");
trigger.position = lct.position;
trigger.halfheight = lct.halfheight;
trigger.radius = lct.radius;
trigger.event = lct.event;
trigger.parent = parentNode;
e.modified<EventTrigger>();
});
ecs.system<LuaBase, LuaEvent>("HandleLuaEvents")
.kind(flecs::OnUpdate)
.each([](LuaBase &base, LuaEvent &evt) {
while (!evt.events.empty()) {
LuaEvent::Event &ev = evt.events.front();
base.mLua->call_handler(ev.event, ev.e1, ev.e2);
evt.events.pop_front();
}
});
}
}

View File

@@ -187,9 +187,11 @@ PhysicsModule::PhysicsModule(flecs::world &ecs)
const CharacterBase &base) {
ZoneScopedN("SetupCharacterPh");
CharacterBody &b = e.ensure<CharacterBody>();
Ogre::SceneNode *n =
ECS::get<CharacterModule>().characterNodes.at(
e);
b.ch.reset(JoltPhysicsWrapper::getSingleton()
.createCharacter(base.mBodyNode,
1.75f, 0.23f));
.createCharacter(n, 1.75f, 0.23f));
if (!e.has<CharacterDisablePhysics>())
static_cast<JPH::Character *>(b.ch.get())
->AddToPhysicsSystem(
@@ -534,8 +536,11 @@ PhysicsModule::PhysicsModule(flecs::world &ecs)
const CharacterBase &chbase,
const CharacterBody &body, CharacterVelocity &gr) {
ZoneScopedN("HandleVelocity");
Ogre::SceneNode *n =
ECS::get<CharacterModule>().characterNodes.at(
e);
if (e.has<InWater>() &&
chbase.mBodyNode->_getDerivedPosition().y > -0.5f)
n->_getDerivedPosition().y > -0.5f)
e.remove<InWater>();
Ogre::Vector3 v = gr.velocity;
v.y = 0.0f;
@@ -572,7 +577,10 @@ PhysicsModule::PhysicsModule(flecs::world &ecs)
.each([this](flecs::entity e, CharacterBase &ch) {
ZoneScopedNC("HandleSubmerge", 0xFF3030);
float full_subm = 2.0f;
Ogre::Vector3 pos = ch.mBodyNode->getPosition();
Ogre::SceneNode *n =
ECS::get<CharacterModule>().characterNodes.at(
e);
Ogre::Vector3 pos = n->getPosition();
float current_subm = -Ogre::Math::Clamp(
pos.y + Ogre::Math::Sin(ch.mTimer * 0.13f +
130.0f) *
@@ -595,23 +603,23 @@ PhysicsModule::PhysicsModule(flecs::world &ecs)
CharacterBase &ch, const CharacterBody &body,
CharacterVelocity &gr) {
ZoneScopedNC("HandleVelocityNoPhysics", 0xFF4040);
Ogre::SceneNode *n =
ECS::get<CharacterModule>().characterNodes.at(
e);
Ogre::Vector3 v = gr.velocity;
// v.y = 0.0f;
ch.mBodyNode->_setDerivedPosition(
ch.mBodyNode->_getDerivedPosition() +
v * eng.delta);
n->_setDerivedPosition(n->_getDerivedPosition() +
v * eng.delta);
gr.velocity = Ogre::Vector3::ZERO;
if (e.has<JPH::BodyID>())
JoltPhysicsWrapper::getSingleton()
.setPositionAndRotation(
e.get<JPH::BodyID>(),
ch.mBodyNode
->_getDerivedPosition(),
ch.mBodyNode
->_getDerivedOrientation(),
n->_getDerivedPosition(),
n->_getDerivedOrientation(),
false);
if (e.has<InWater>() &&
ch.mBodyNode->_getDerivedPosition().y > -0.5f) {
n->_getDerivedPosition().y > -0.5f) {
e.remove<InWater>();
ch.is_submerged = false;
ZoneTextF("remove in water");
@@ -646,26 +654,27 @@ void PhysicsModule::controlPhysics(flecs::entity e, bool enable)
OgreAssert(e.has<CharacterBody>(), "No body component");
OgreAssert(e.has<JPH::BodyID>(),
"No body id in entity");
}
if (!JoltPhysicsWrapper::getSingleton().isAdded(
e.get<JPH::BodyID>())) {
Ogre::Vector3 position =
e.get<CharacterBase>()
.mBodyNode->_getDerivedPosition();
Ogre::Quaternion orientation =
e.get<CharacterBase>()
.mBodyNode->_getDerivedOrientation();
if (position.y >= -0.5f)
e.remove<InWater>();
JoltPhysicsWrapper::getSingleton()
.setPositionAndRotation(e.get<JPH::BodyID>(),
position, orientation,
false);
JoltPhysicsWrapper::getSingleton().addBody(
e.get<JPH::BodyID>(),
JPH::EActivation::Activate);
}
} else {
Ogre::SceneNode *n =
ECS::get<CharacterModule>().characterNodes.at(
e);
if (!JoltPhysicsWrapper::getSingleton().isAdded(
e.get<JPH::BodyID>())) {
Ogre::Vector3 position =
n->_getDerivedPosition();
Ogre::Quaternion orientation =
n->_getDerivedOrientation();
if (position.y >= -0.5f)
e.remove<InWater>();
JoltPhysicsWrapper::getSingleton()
.setPositionAndRotation(
e.get<JPH::BodyID>(), position,
orientation, false);
JoltPhysicsWrapper::getSingleton().addBody(
e.get<JPH::BodyID>(),
JPH::EActivation::Activate);
}
}
} else {
if (e.has<CharacterBase>()) {
e.add<CharacterDisablePhysics>();
if (!e.has<CharacterBody>())
@@ -673,14 +682,16 @@ void PhysicsModule::controlPhysics(flecs::entity e, bool enable)
OgreAssert(e.has<CharacterBody>(), "No body component");
OgreAssert(e.has<JPH::BodyID>(),
"No body id in entity");
}
if (JoltPhysicsWrapper::getSingleton().isAdded(
e.get<JPH::BodyID>()))
JoltPhysicsWrapper::getSingleton().removeBody(
e.get<JPH::BodyID>());
Ogre::Vector3 position =
e.get<CharacterBase>().mBodyNode->_getDerivedPosition();
e.remove<InWater>();
Ogre::SceneNode *n =
ECS::get<CharacterModule>().characterNodes.at(
e);
if (JoltPhysicsWrapper::getSingleton().isAdded(
e.get<JPH::BodyID>()))
JoltPhysicsWrapper::getSingleton().removeBody(
e.get<JPH::BodyID>());
Ogre::Vector3 position = n->_getDerivedPosition();
e.remove<InWater>();
}
}
}
bool PhysicsModule::raycastQuery(const Ogre::Vector3 &startPos,

View File

@@ -318,10 +318,12 @@ PlayerActionModule::PlayerActionModule(flecs::world &ecs)
ECS::get<CharacterManagerModule>()
.getPlayer();
if (player.is_valid()) {
Ogre::SceneNode *n =
ECS::get<CharacterModule>()
.characterNodes.at(
player);
Ogre::Vector3 playerPos =
player.get<CharacterBase>()
.mBodyNode
->_getDerivedPosition();
n->_getDerivedPosition();
list.UIquery(playerPos);
} else {
list.UIquery(cameraPos);
@@ -479,10 +481,11 @@ out:;
anode.position +
anode.rotation * placeLocalOffset[place];
if (ch.is_valid() && ch.has<CharacterBase>()) {
ch.get<CharacterBase>()
.mBodyNode->_setDerivedOrientation(newRotation);
ch.get<CharacterBase>().mBodyNode->_setDerivedPosition(
newPosition);
Ogre::SceneNode *n =
ECS::get<CharacterModule>().characterNodes.at(
ch);
n->_setDerivedOrientation(newRotation);
n->_setDerivedPosition(newPosition);
}
if (actor >= 0) {
town.get_mut<TownNPCs>().npcs[actor].position =

View File

@@ -45,7 +45,10 @@ SlotsModule::SlotsModule(flecs::world &ecs)
slot.removeSlot(e);
return;
}
slot.addChild(ch.mBodyNode);
Ogre::SceneNode *n =
ECS::get<CharacterModule>()
.characterNodes.at(e);
slot.addChild(n);
slot.createSlotData(e);
std::cout << "base: "
<< slot.getSlotBase()->getName();