extend_texture_(1024_2048)Sims4 (non send to, interactive cmd):
--------------------------------------------------------------

extend_texture_(1024_2048)Sims4
---

@echo off
echo Drag and drop the OLD texture file here, then press Enter:
set /p OLD_TEX=
echo Drag and drop the NEW (extended) texture file here, then press Enter:
set /p NEW_TEX=
echo Drag and drop the FBX file here, then press Enter:
set /p FBX_FILE=
:: Path to Blender executable
set "BLENDER_EXE=C:\portableapps\blender36\blender.exe"
:: Run Blender in background with Python script
"%BLENDER_EXE%" --background --python replace_fbx_texture_blender.py -- "%FBX_FILE%" "%OLD_TEX%" "%NEW_TEX%"
pause



replace_fbx_texture_blender.py
---

import bpy
import sys
import os
argv = sys.argv
argv = argv[argv.index("--") + 1:]  # arguments after --
if len(argv) != 3:
    print("Usage: blender --background --python replace_fbx_texture_blender.py -- FBX_FILE OLD_TEX NEW_TEX")
    sys.exit(1)
fbx_path = os.path.abspath(argv[0])
old_tex_path = os.path.abspath(argv[1])
new_tex_path = os.path.abspath(argv[2])
bpy.ops.wm.read_factory_settings(use_empty=True)
bpy.ops.import_scene.fbx(filepath=fbx_path)
for mat in bpy.data.materials:
    if mat.node_tree:
        for node in mat.node_tree.nodes:
            if node.type == 'TEX_IMAGE' and node.image:
                img_path = bpy.path.abspath(node.image.filepath)
                if os.path.normcase(img_path) == os.path.normcase(old_tex_path):
                    print(f"Replacing texture in material: {mat.name}")
                    node.image.filepath = new_tex_path
                    node.image.reload()
# Original old texture size
old_w, old_h = 519, 507
# New extended texture size
new_w, new_h = 1024, 2048

scale_x = old_w / new_w
scale_y = old_h / new_h
offset_x = 0
offset_y = 1 - scale_y  # top-left corner

for obj in bpy.data.objects:
    if obj.type == 'MESH':
        mesh = obj.data
        for uv_layer in mesh.uv_layers:
            for uv in uv_layer.data:
                # Scale and offset UV
                uv.uv.x = uv.uv.x * scale_x + offset_x
                uv.uv.y = uv.uv.y * scale_y + offset_y
fbx_dir, fbx_name = os.path.split(fbx_path)
new_fbx_name = os.path.splitext(fbx_name)[0] + "_new.fbx"
new_fbx_path = os.path.join(fbx_dir, new_fbx_name)

bpy.ops.export_scene.fbx(filepath=new_fbx_path, embed_textures=False)
print(f"Saved new FBX to: {new_fbx_path}")




Show info on blend file:
-------------------------

@echo off
set BLEND_FILE=C:\Users\User\Desktop\out.blend
set BLENDER_EXE=C:\portableapps\blender36\blender.exe

"%BLENDER_EXE%" --factory-startup -b "%BLEND_FILE%" --python-expr "import bpy; print('=== OBJECTS ==='); [print(o.name, o.type) for o in bpy.data.objects]; print('=== MESHES ==='); [print(m.name, 'UVs:', [uv.name for uv in m.uv_layers], 'Mats:', [mat.name for mat in m.materials]) for m in bpy.data.meshes]; print('=== ARMATURES ==='); [print(a.name) for a in bpy.data.armatures]"

pause



Remove bones from existing fbx:
------------------------------


@echo off
set "BLENDER_EXE=C:\portableapps\blender36\blender.exe"
set "FBX_FILE=C:\Users\User\Desktop\in.fbx"
set "OUTPUT_FILE=C:\Users\User\Desktop\in_nobones.fbx"

echo Removing bones from: %FBX_FILE%

set "PYTHON_SCRIPT=%TEMP%\blender_fix_%RANDOM%.py"

(
echo import bpy
echo import os
echo.
echo bpy.ops.wm.read_factory_settings(use_empty=True^)
echo.
echo bpy.ops.import_scene.fbx(filepath=r'%FBX_FILE:\=\\%'^)
echo.
echo for obj in bpy.data.objects:
echo     if obj.type == 'MESH':
echo         bpy.context.view_layer.objects.active = obj
echo         bpy.ops.object.transform_apply(location=False, rotation=True, scale=True^)
echo.
echo armatures = [obj for obj in bpy.data.objects if obj.type == 'ARMATURE'^]
echo bpy.ops.object.select_all(action='DESELECT'^)
echo for arm in armatures:
echo     arm.select_set(True^)
echo if armatures:
echo     bpy.ops.object.delete(^)
echo.
echo bpy.ops.export_scene.fbx(filepath=r'%OUTPUT_FILE:\=\\%'^)
echo print('Saved:', r'%OUTPUT_FILE:\=\\%'^)
) > "%PYTHON_SCRIPT%"

"%BLENDER_EXE%" --background --python "%PYTHON_SCRIPT%"
del "%PYTHON_SCRIPT%" >nul 2>&1

echo.
echo Done! Check: %OUTPUT_FILE%
pause


Facege (3.5) obj to fbx with blendshapes convert
------------------------------------------------

import bpy
import os
import glob

# File paths (Update if needed)
sf_folder = r"C:\Users\User\Desktop\sf"
base_mesh_path = os.path.join(sf_folder, "ls.obj")
output_fbx_path = os.path.join(sf_folder, "ls.fbx")

# Clear existing objects in the scene
bpy.ops.object.select_all(action='SELECT')
bpy.ops.object.delete()

# Import the base mesh (ls.obj)
bpy.ops.import_scene.obj(filepath=base_mesh_path)
base_obj = bpy.context.selected_objects[0]
base_obj.name = "BaseMesh"

# Find all blendshape files (expressions, modifiers, phonemes)
blendshape_files = glob.glob(os.path.join(sf_folder, "ls_Expression*.obj")) + \
                   glob.glob(os.path.join(sf_folder, "ls_Modifier*.obj")) + \
                   glob.glob(os.path.join(sf_folder, "ls_Phoneme*.obj"))

# Process each blendshape
for blendshape_path in blendshape_files:
    blendshape_name = os.path.basename(blendshape_path).replace(".obj", "")

    # Import blendshape mesh
    bpy.ops.import_scene.obj(filepath=blendshape_path)
    blendshape_obj = bpy.context.selected_objects[0]

    # Create Basis shape key if not exists
    if base_obj.data.shape_keys is None:
        base_obj.shape_key_add(name="Basis")

    # Create new shape key
    shape_key = base_obj.shape_key_add(name=blendshape_name)
    
    # Transfer vertex positions
    for i, vert in enumerate(blendshape_obj.data.vertices):
        shape_key.data[i].co = vert.co

    # Remove the imported blendshape mesh
    bpy.data.objects.remove(blendshape_obj, do_unlink=True)

# Export the final FBX
bpy.ops.export_scene.fbx(filepath=output_fbx_path, use_selection=False, apply_unit_scale=True)

print("FBX exported successfully:", output_fbx_path)



Hardlink mesh parts
-------------------

import bpy

# Create a new mesh object
def join_all_meshes():
    # Get all mesh objects in the scene
    mesh_objects = [obj for obj in bpy.context.scene.objects if obj.type == 'MESH']
    
    if not mesh_objects:
        print("No mesh objects found in the scene.")
        return

    # Select all mesh objects
    bpy.ops.object.select_all(action='DESELECT')
    for obj in mesh_objects:
        obj.select_set(True)

    # Set the active object to the first mesh object
    bpy.context.view_layer.objects.active = mesh_objects[0]

    # Join all selected mesh objects
    bpy.ops.object.join()

# Run the function
join_all_meshes()



Make mesh "single user" (to allow CATS at some times)
------------------------------------

import bpy

# Iterate through all mesh objects
for obj in bpy.data.objects:
    if obj.type == 'MESH':  # Check if the object is a mesh
        obj.data = obj.data.copy()  # Make mesh data single-user

print("All meshes are now single-user.")




Extract current loaded model textures
--------------------------------------

import bpy
import os
import shutil

# Define the destination directory
output_dir = r"C:\Users\User\Desktop\out"

# Ensure the output directory exists
os.makedirs(output_dir, exist_ok=True)

# Function to extract textures
def extract_textures():
    for material in bpy.data.materials:
        if not material.use_nodes:
            continue  # Skip materials that don't use nodes

        for node in material.node_tree.nodes:
            if node.type == 'TEX_IMAGE':
                image = node.image
                if image and image.filepath:
                    # Get the source file path
                    source_path = bpy.path.abspath(image.filepath)
                    
                    # Ensure the source file exists
                    if os.path.exists(source_path):
                        # Copy the texture to the output directory
                        destination_path = os.path.join(output_dir, os.path.basename(source_path))
                        shutil.copy2(source_path, destination_path)
                        print(f"Copied: {source_path} -> {destination_path}")
                    else:
                        print(f"File not found: {source_path}")

# Run the texture extraction
extract_textures()
print("Texture extraction completed.")




Erase all Textures
------------------


import bpy

# Iterate through all materials in the scene
for material in bpy.data.materials:
    if material.use_nodes:  # Check if the material uses nodes
        # Access the node tree of the material
        nodes = material.node_tree.nodes
        # Iterate through all nodes in the node tree
        for node in nodes:
            # Check if the node is an Image Texture node
            if node.type == 'TEX_IMAGE':
                # Remove the node from the material
                nodes.remove(node)

# Optionally, clear unused images from Blender's data
for image in bpy.data.images:
    bpy.data.images.remove(image)
    
print("All textures have been erased.")



Convert Texture to Vertex Color (currently not working)
-------------------------------------------------------

def execute(self, context):
    obdata = context.object.data

    if self.replace_active_layer and obdata.vertex_colors.active:
        vertex_colors = obdata.vertex_colors.active
    else:
        vertex_colors = obdata.vertex_colors.new(name="Baked UV texture")
    
        if not vertex_colors:
            # Can't add more than 17 VCol layers
            self.report({'ERROR'}, "Couldn't add another Vertex Color layer,\n"
                                   "Please remove an existing layer or replace active.")
            return {'CANCELLED'}
        
    obdata.vertex_colors.active = vertex_colors

    uv_images = {}
    for uv_tex in obdata.uv_textures.active.data:
        if uv_tex.image and uv_tex.image.name not in uv_images and uv_tex.image.pixels:
            
            uv_images[uv_tex.image.name] = (uv_tex.image.size[0],
                                            uv_tex.image.size[1],
                                            uv_tex.image.pixels[:]
                                           )
    
    for p in obdata.polygons:
        img = obdata.uv_textures.active.data[p.index].image
        if not img:
            continue
        
        image_size_x, image_size_y, uv_pixels = uv_images[img.name]
        
        for loop in p.loop_indices:
    
            co = obdata.uv_layers.active.data[loop].uv
            x_co = round(co[0] * (image_size_x - 1))
            y_co = round(co[1] * (image_size_y - 1))
            
            if x_co < 0 or x_co >= image_size_x or y_co < 0 or y_co >= image_size_y:
                if self.mappingMode == 'CLIP':
                    continue

                elif self.mappingMode == 'REPEAT':
                    x_co %= image_size_x
                    y_co %= image_size_y

                elif self.mappingMode == 'EXTEND':
                    if x_co > image_size_x - 1:
                        x_co = image_size_x - 1
                    if x_co < 0:
                        x_co = 0
                    if y_co > image_size_y - 1:
                        y_co = image_size_y - 1
                    if y_co < 0:
                        y_co = 0
            
            if self.mirror_x:
                 x_co = image_size_x -1 - x_co
                 
            if self.mirror_y:
                 y_co = image_size_y -1 - y_co
                 
            col_out = vertex_colors.data[loop].color
                
            pixelNumber = (image_size_x * y_co) + x_co
            r = uv_pixels[pixelNumber*4]
            g = uv_pixels[pixelNumber*4 + 1]
            b = uv_pixels[pixelNumber*4 + 2]
            a = uv_pixels[pixelNumber*4 + 3]
            
            col_in = r, g, b  # texture-color
            col_result = [r, g, b]  # existing / 'base' color
            
            if self.blendingMode == 'MIX':
                col_result = col_in
                
            # Add more blending modes here...

            # Add alpha color
            a_inverted = 1 - a
            alpha_color = context.scene.uv_bake_alpha_color
            col_result = (col_result[0] * a + alpha_color[0] * a_inverted,
                          col_result[1] * a + alpha_color[1] * a_inverted,
                          col_result[2] * a + alpha_color[2] * a_inverted)
            
            vertex_colors.data[loop].color = col_result

    # Remove the textures from the UV layer
    for uv_tex in obdata.uv_textures:
        uv_tex.active = False
        obdata.uv_textures.remove(uv_tex)

    # Remove the material texture links (both in node-based materials and legacy texture slots)
    for mat in context.object.data.materials:
        if mat.use_nodes:
            # If the material uses nodes, search for texture nodes
            for node in mat.node_tree.nodes:
                if node.type == 'TEX_IMAGE':  # Image Texture node
                    # Unlink the image texture from the material node
                    node.image = None
        else:
            # If the material doesn't use nodes, check the texture slots
            for i, texture_slot in enumerate(mat.texture_slots):
                if texture_slot:
                    # Remove the texture
                    mat.texture_slots[i] = None

    return {'FINISHED'}



Accurig (fbx) to vrm
--------------------



====Exporter Py====

# Check it works at Blender 3.4.1
# You can change the path and name
filepathjson = "C:\\portableapps\\blender36\\scripts\\humanbody2.json"
filepathVRM = "C:\\portableapps\\blender36\\scripts\\VRM"
filenameVRM = "VRM"

# Main code is below here line.

import os
import bpy

# Ensure the VRM directory exists
try:
    os.makedirs(filepathVRM)
except FileExistsError:
    pass

# Deselect all objects in the scene
bpy.ops.object.select_all(action='DESELECT')

# Check if "Armature" exists before selecting it
if "Armature" in bpy.data.objects:
    object = bpy.data.objects["Armature"]
    object.select_set(True)
else:
    print("Object 'Armature' not found in the scene.")

# Rename bones for the armature
for obj in bpy.data.objects:
    if obj.type == 'ARMATURE':
        for bone in obj.pose.bones:
            bone.name = bone.name.replace("CC_Base_", "HumanBodyBones.")
            
        for bone in obj.pose.bones:
            if obj.name != "RL_BoneRoot":
                bone.name = bone.name.replace("L_", "Left")
            
        for bone in obj.pose.bones:
            bone.name = bone.name.replace("R_", "Right")

        for bone in obj.pose.bones:
            bone.name = bone.name.replace("Clavicle", "Shoulder")
                     
        for bone in obj.pose.bones:
            bone.name = bone.name.replace("Fore", "Lower")
                     
        for bone in obj.pose.bones:
            bone.name = bone.name.replace("ToeBase", "Toes")
            
        for bone in obj.pose.bones:
            bone.name = bone.name.replace("arm", "Arm")

        for bone in obj.pose.bones:
            bone.name = bone.name.replace("Thigh", "UpperLeg")

        for bone in obj.pose.bones:
            bone.name = bone.name.replace("Calf", "LowerLeg")

# Load human bone mappings from JSON
bpy.ops.vrm.load_human_bone_mappings(filepath=filepathjson)

# Deselect all objects again
bpy.ops.object.select_all(action='DESELECT')

# Check if "default" object exists before trying to select it
if "default" in bpy.data.objects:
    object = bpy.data.objects["default"]
    object.select_set(True)
else:
    print("Object 'default' not found in the scene.")

# Select all mesh objects (assuming you want to select all of them, not just 'default')
selectob = bpy.data.objects
for ob in selectob:
    if ob.type == 'MESH':
        ob.select_set(True)
        bpy.context.view_layer.objects.active = ob

# Switch to Shader Node Tree for material editing
bpy.context.area.ui_type = 'ShaderNodeTree'
bpy.context.object.active_material_index = 0
mat = bpy.context.active_object.material_slots[0].material

# Node tree for material setup
node_tree = bpy.context.active_object.material_slots[0].material.node_tree
nodes = node_tree.nodes
links = node_tree.links

# Create MToon Shader Node Group
mtoon = nodes.new("ShaderNodeGroup")
mtoon.node_tree = bpy.data.node_groups['MToon_unversioned']

# Get the Image Texture node
dif = node_tree.nodes["Image Texture"]

# Print and check nodes for debugging
print(dif)
for n in nodes:
    print(n)

# Get the Material Output node and link everything up
out1 = node_tree.nodes["Material Output"]
for l in links:
    links.remove(l)

# Link nodes
links.new(dif.outputs[0], mtoon.inputs[0])
links.new(mtoon.outputs[0], out1.inputs[0])

# Enable backface culling and set blend method
bpy.context.object.active_material.use_backface_culling = True
bpy.context.object.active_material.blend_method = 'OPAQUE'

# Export the scene to a VRM file
bpy.ops.export_scene.vrm(filepath=os.path.join(filepathVRM, filenameVRM + ".vrm"))

# Switch to Object mode
bpy.ops.object.mode_set(mode='OBJECT', toggle=False)



======human2body.json========


{
    "chest": "HumanBodyBones.Spine02",
    "head": "HumanBodyBones.Head",
    "hips": "HumanBodyBones.Hip",
    "jaw": "HumanBodyBones.JawRoot",
    "leftEye": "HumanBodyBones.LeftEye",
    "leftFoot": "HumanBodyBones.LeftFoot",
    "leftHand": "HumanBodyBones.LeftHand",
    "leftIndexDistal": "HumanBodyBones.LeftIndex3",
    "leftIndexIntermediate": "HumanBodyBones.LeftIndex2",
    "leftIndexProximal": "HumanBodyBones.LeftIndex1",
    "leftLittleDistal": "HumanBodyBones.LeftPinky3",
    "leftLittleIntermediate": "HumanBodyBones.LeftPinky2",
    "leftLittleProximal": "HumanBodyBones.LeftPinky1",
    "leftLowerArm": "HumanBodyBones.LeftLowerArm",
    "leftLowerLeg": "HumanBodyBones.LeftLowerLeg",
    "leftMiddleDistal": "HumanBodyBones.LeftMid3",
    "leftMiddleIntermediate": "HumanBodyBones.LeftMid2",
    "leftMiddleProximal": "HumanBodyBones.LeftMid1",
    "leftRingDistal": "HumanBodyBones.LeftRing3",
    "leftRingIntermediate": "HumanBodyBones.LeftRing2",
    "leftRingProximal": "HumanBodyBones.LeftRing1",
    "leftShoulder": "HumanBodyBones.LeftShoulder",
    "leftThumbIntermediate": "HumanBodyBones.LeftThumb2",
    "leftThumbProximal": "HumanBodyBones.LeftThumb1",
    "leftToes": "HumanBodyBones.LeftToes",
    "leftUpperArm": "HumanBodyBones.LeftUpperArm",
    "leftUpperLeg": "HumanBodyBones.LeftUpperLeg",
    "neck": "HumanBodyBones.NeckTwist01",
    "rightEye": "HumanBodyBones.RightEye",
    "rightFoot": "HumanBodyBones.RightFoot",
    "rightHand": "HumanBodyBones.RightHand",
    "rightIndexDistal": "HumanBodyBones.RightIndex3",
    "rightIndexIntermediate": "HumanBodyBones.RightIndex2",
    "rightIndexProximal": "HumanBodyBones.RightIndex1",
    "rightLittleDistal": "HumanBodyBones.RightPinky3",
    "rightLittleIntermediate": "HumanBodyBones.RightPinky2",
    "rightLittleProximal": "HumanBodyBones.RightPinky1",
    "rightLowerArm": "HumanBodyBones.RightLowerArm",
    "rightLowerLeg": "HumanBodyBones.RightLowerLeg",
    "rightMiddleDistal": "HumanBodyBones.RightMid3",
    "rightMiddleIntermediate": "HumanBodyBones.RightMid2",
    "rightMiddleProximal": "HumanBodyBones.RightMid1",
    "rightRingDistal": "HumanBodyBones.RightRing3",
    "rightRingIntermediate": "HumanBodyBones.RightRing2",
    "rightRingProximal": "HumanBodyBones.RightRing1",
    "rightShoulder": "HumanBodyBones.RightShoulder",
    "rightThumbIntermediate": "HumanBodyBones.RightThumb2",
    "rightThumbProximal": "HumanBodyBones.RightThumb1",
    "rightToes": "HumanBodyBones.RightToes",
    "rightUpperArm": "HumanBodyBones.RightUpperArm",
    "rightUpperLeg": "HumanBodyBones.RightUpperLeg",
    "spine": "HumanBodyBones.Spine01"
}



NOESIS convert xyz2pmx [via send-to.bat]
-----------------------
@echo off
:: Ensure an input file is provided
if "%~1"=="" (
echo No file provided. Please use this script via the "Send To" menu.
pause
exit /b
)
SET "INPUT_FILE=%~1"
SET "NOESIS_PATH=C:\portableapps\Noesis\Noesis.exe"
:: Check if the Noesis executable exists
if not exist "%NOESIS_PATH%" (
echo Noesis executable not found at "%NOESIS_PATH%".
echo Please check the path and try again.
pause
exit /b
)
SET "OUTPUT_FILE=%~1.pmx"
echo "%NOESIS_PATH%" ?cmode "%INPUT_FILE%" "%OUTPUT_FILE%" -flipax 2 -scale 1 #uncomment for pmx2fbx fix
"%NOESIS_PATH%" ?cmode "%INPUT_FILE%" "%OUTPUT_FILE%"
if %errorlevel%==0 (
echo Export successful! File processed: "%INPUT_FILE%"
echo Output file: "%OUTPUT_FILE%"
) else (
echo Export failed. Please check the input file and Noesis settings.
)

Embed texture in fbx  [via send-to.bat]
---------------------


======fbx_embed_texture.bat==

@echo off
setlocal

REM Path to Blender executable
set BLENDER_PATH="C:\portableapps\blender36\blender.exe"

REM Check if a file is provided
if "%~1"=="" (
    echo No FBX file provided.
    echo Usage: Send to embed_fbx.bat
    pause
    exit /b 1
)

REM Get input FBX file and define output FBX file path
set INPUT_FILE=%~1
set OUTPUT_FILE=%~dpn1_embedded.fbx

REM Run Blender with the Python script
%BLENDER_PATH% --background --python C:\Users\User\AppData\Roaming\Microsoft\Windows\SendTo\extra\embed_fbx_textures.py -- "%INPUT_FILE%" "%OUTPUT_FILE%"

if errorlevel 1 (
    echo Failed to embed textures for %INPUT_FILE%.
    pause
    exit /b 1
)

echo Successfully embedded textures into: %OUTPUT_FILE%
pause


=== /extra/ embed_fbx_textures.py ==

import bpy
import sys

# Get the FBX file paths from the arguments
input_fbx = sys.argv[-2]
output_fbx = sys.argv[-1]

# Clear any existing objects
bpy.ops.object.select_all(action='SELECT')
bpy.ops.object.delete(use_global=False)

# Import the FBX file
bpy.ops.import_scene.fbx(filepath=input_fbx)

# Pack all external resources (textures) into the blend file
bpy.ops.file.pack_all()

# Export the FBX file with embedded textures
bpy.ops.export_scene.fbx(filepath=output_fbx, embed_textures=True)

print(f"Successfully embedded textures into: {output_fbx}")



Open fbx in blender (GUI Mode) [via send-to.bat]
----------------------------------------------

@echo off
SET FBX_FILE=%1
SET BLENDER_PATH="C:\portableapps\blender36\blender.exe"
echo import sys > temp_script.py
echo import bpy >> temp_script.py
echo fbx_file = sys.argv[-1] >> temp_script.py
echo bpy.ops.import_scene.fbx(filepath=fbx_file) >> temp_script.py
%BLENDER_PATH% --python temp_script.py -- %FBX_FILE%
del temp_script.py
