Both `Transform._parentChange` and `UITransform._parentChange` relied on
internal helpers (`_updateAllWorldFlag` / `_updateWorldFlagWithParentRectChange`)
that early-exit when the self-entity already has all target world dirty flags
set. Combined with `_parentTransformCache` potentially resolved to `null` during
partial clone/instantiate construction, a reparented subtree could keep
descendants' `worldMatrix` cached at identity — e.g. a UI layer rendered at
(0, 0) instead of inheriting the new parent's world position.
Both methods now delegate to new `_propagateReparentDirty(UI)` helpers which
unconditionally recurse over the whole subtree, setting `_worldAssociatedChange`
flags and `_isParentDirty = true` on every descendant so that subsequent
`_getParentTransform()` calls re-resolve the parent chain. UITransform's
alignment-driven size/position recompute path is preserved.
Observed in Galacean engine 2.0.0-alpha.24 when a prefab was pool-instantiated,
its sub-tree world properties were accessed (caching `null` parent), then the
prefab was `addChild`-ed under a positioned parent.
Added three white-box tests in `tests/src/core/Transform.test.ts` covering
(a) reparent after clone + child world access, (b) self-has-all-world-flags-set
early-exit case, (c) descendant cached null parent case. RED → GREEN verified.
Single-root GLTF scenes no longer wrap the root node in a GLTF_ROOT
container, which avoids redundant nesting and fixes animation path
resolution for models like Mixamo characters.
- GLTFSceneParser: single-root scenes use the node directly as scene root
- GLTFAnimationParser: remove single-root path prefixing (no longer needed)
- Entity.findByPath: prefer real same-name child over legacy self-name prefix
- Add AGENTS.md to .gitignore
- animator-blendShape: use getComponentsIncludeChildren to find
SkinnedMeshRenderer on child entity
- animator-multiSubMeshBlendShape: place Animator on actual model
root (children[0]) so curve bindings with empty path resolve correctly
Remove single-root vs multi-root branching in GLTFSceneParser, always
creating a GLTF_ROOT wrapper entity. This ensures animation bone paths
are consistent across different glTF files, fixing cross-file animation
clip retargeting.
When sizeMode is set to Automatic, the UITransform size is automatically
synchronized to the sprite's natural dimensions when the sprite changes.
This matches Cocos Creator's Sprite.SizeMode.TRIMMED behavior.
- Add SpriteSizeMode enum (Custom / Automatic)
- Add sizeMode property to Image with getter/setter
- Sync UITransform.size in set sprite and _onSpriteChange
- Export SpriteSizeMode from component index
AnimatorState.speed is part of the shared AnimatorController asset.
Modifying it at runtime pollutes all Animator instances sharing the
same controller, causing animation speed corruption after cloning.
- Add speed field to AnimatorStatePlayData, initialized from AnimatorState.speed on reset
- Add proxy properties (name/clip/wrapMode/transitions/addStateMachineScript)
- Change speed calculation to playData.speed * animator.speed
- findAnimatorState now returns per-instance AnimatorStatePlayData
- Export AnimatorStatePlayData for consumer code
Verify that screenPointToRay and viewport-world round-trip produce
correct results when the camera inherits non-identity scale from a
parent entity. Without the fix, the round-trip deviates by the
inherited scale factor (e.g. 105 -> 107.5 at scale 1.5).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The viewMatrix getter intentionally ignores the camera entity's world scale
(using Matrix.rotationTranslation), but _getInvViewProjMat() was using the
full entity.transform.worldMatrix which includes inherited scale. This
inconsistency causes screenPointToRay to produce incorrect world-space rays
when the camera inherits scale from a parent entity (e.g. UICanvas in
ScreenSpaceCamera mode with camera as a child of canvas).
Closes#2748
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
GLES100 visitJumpStatement converted `return expr;` to `gl_FragColor = expr`
without a trailing semicolon, causing WebGL compilation errors. Only triggered
when fragment entry returns vec4 (Cocos pattern), not void (standard Galacean).
- Suppress `uniform` output for global struct-typed variables (e.g. `Varyings o;`)
- Register global struct vars in both per-function and cross-stage maps
- Unify macro member access scanning into callback-based _forEachMacroMemberAccess
- Add registerStructVar() encapsulation in VisitorContext
- Add Cocos VSOutput pattern test (global-varying-var)
Build a combined _globalStructVarMap in visitShaderProgram by scanning
both vertex and fragment entry functions, so global #define values like
`attr.POSITION` or `o.v_uv` are correctly transformed in all stages.
Rewrite define-struct-access tests to use snapshot file comparison
against expected/ GLSL outputs for clearer verification.
Add assertions for macro usage in expressions (not just #define transformation):
- Macro as RHS in multiplication, as LHS in assignment
- Macro as function argument in dot(), texture2D()
- Multiple varying properties (v_uv, v_normal) referenced via #define
Verify the actual CodeGen output instead of just checking GLSL compilation:
- #define values with struct member access are correctly transformed
- varying/attribute declarations are emitted for referenced properties
When #define values contain struct member access like `o.v_uv` (where `o` is
a Varyings/Attributes/MRT struct variable), the CodeGen now correctly transforms
them to just the property name (e.g. `v_uv`), matching the behavior of direct
struct member access in regular code. Closes#2944.
When a builtin generic function (e.g. normalize) receives TypeAny args,
resolvedReturnType stays TypeAny. Previously the else branch returned
the raw EGenType enum value (200), which is neither a concrete type nor
a wildcard, causing downstream user-function overload matching to fail.
- Simplify resolveGenericReturnType: remove genericParamType param, only
check if return type is GVec4
- Fix textureCube/textureCubeLod return type: SAMPLER_CUBE → VEC4
- Add missing texture2DLod builtin function registration
- Add texture2DLod test cases to texture-generic.shader
texture(sampler2D, vec2) returns GVec4 which was incorrectly resolved to
the sampler type instead of vec4, causing "No overload function type found"
when passing the result to user-defined functions like decode32(vec4).
Add resolveGenericReturnType() to correctly map GSampler* → GVec4:
sampler2D/sampler3D/samplerCube → vec4
isampler2D/isampler3D/... → ivec4
usampler2D/usampler3D/... → uvec4
When a builtin generic function (e.g. normalize) receives TypeAny args,
resolvedReturnType stays TypeAny. Previously the else branch returned
the raw EGenType enum value (200), which is neither a concrete type nor
a wildcard, causing downstream user-function overload matching to fail.
- Simplify resolveGenericReturnType: remove genericParamType param, only
check if return type is GVec4
- Fix textureCube/textureCubeLod return type: SAMPLER_CUBE → VEC4
- Add missing texture2DLod builtin function registration
- Add texture2DLod test cases to texture-generic.shader
texture(sampler2D, vec2) returns GVec4 which was incorrectly resolved to
the sampler type instead of vec4, causing "No overload function type found"
when passing the result to user-defined functions like decode32(vec4).
Add resolveGenericReturnType() to correctly map GSampler* → GVec4:
sampler2D/sampler3D/samplerCube → vec4
isampler2D/isampler3D/... → ivec4
usampler2D/usampler3D/... → uvec4